2007-06-12 21:07:21 +08:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2007 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.
|
|
|
|
*/
|
|
|
|
|
2008-04-26 04:53:30 +08:00
|
|
|
#include <linux/kernel.h>
|
2008-02-21 01:07:25 +08:00
|
|
|
#include <linux/bio.h>
|
2007-06-12 18:35:45 +08:00
|
|
|
#include <linux/buffer_head.h>
|
2008-05-03 02:43:14 +08:00
|
|
|
#include <linux/file.h>
|
2007-06-12 18:35:45 +08:00
|
|
|
#include <linux/fs.h>
|
|
|
|
#include <linux/pagemap.h>
|
|
|
|
#include <linux/highmem.h>
|
|
|
|
#include <linux/time.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/string.h>
|
|
|
|
#include <linux/backing-dev.h>
|
|
|
|
#include <linux/mpage.h>
|
|
|
|
#include <linux/swap.h>
|
|
|
|
#include <linux/writeback.h>
|
|
|
|
#include <linux/statfs.h>
|
|
|
|
#include <linux/compat.h>
|
2007-06-16 01:50:00 +08:00
|
|
|
#include <linux/bit_spinlock.h>
|
2007-11-17 00:45:54 +08:00
|
|
|
#include <linux/xattr.h>
|
2008-07-25 00:16:36 +08:00
|
|
|
#include <linux/posix_acl.h>
|
2008-10-31 02:25:28 +08:00
|
|
|
#include <linux/falloc.h>
|
2008-11-20 23:22:27 +08:00
|
|
|
#include "compat.h"
|
2007-06-12 18:35:45 +08:00
|
|
|
#include "ctree.h"
|
|
|
|
#include "disk-io.h"
|
|
|
|
#include "transaction.h"
|
|
|
|
#include "btrfs_inode.h"
|
|
|
|
#include "ioctl.h"
|
|
|
|
#include "print-tree.h"
|
2008-03-25 03:01:56 +08:00
|
|
|
#include "volumes.h"
|
2008-07-18 00:53:50 +08:00
|
|
|
#include "ordered-data.h"
|
2008-08-28 18:21:17 +08:00
|
|
|
#include "xattr.h"
|
2008-09-06 04:13:11 +08:00
|
|
|
#include "tree-log.h"
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
#include "compression.h"
|
Btrfs: Change btree locking to use explicit blocking points
Most of the btrfs metadata operations can be protected by a spinlock,
but some operations still need to schedule.
So far, btrfs has been using a mutex along with a trylock loop,
most of the time it is able to avoid going for the full mutex, so
the trylock loop is a big performance gain.
This commit is step one for getting rid of the blocking locks entirely.
btrfs_tree_lock takes a spinlock, and the code explicitly switches
to a blocking lock when it starts an operation that can schedule.
We'll be able get rid of the blocking locks in smaller pieces over time.
Tracing allows us to find the most common cause of blocking, so we
can start with the hot spots first.
The basic idea is:
btrfs_tree_lock() returns with the spin lock held
btrfs_set_lock_blocking() sets the EXTENT_BUFFER_BLOCKING bit in
the extent buffer flags, and then drops the spin lock. The buffer is
still considered locked by all of the btrfs code.
If btrfs_tree_lock gets the spinlock but finds the blocking bit set, it drops
the spin lock and waits on a wait queue for the blocking bit to go away.
Much of the code that needs to set the blocking bit finishes without actually
blocking a good percentage of the time. So, an adaptive spin is still
used against the blocking bit to avoid very high context switch rates.
btrfs_clear_lock_blocking() clears the blocking bit and returns
with the spinlock held again.
btrfs_tree_unlock() can be called on either blocking or spinning locks,
it does the right thing based on the blocking bit.
ctree.c has a helper function to set/clear all the locked buffers in a
path as blocking.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2009-02-04 22:25:08 +08:00
|
|
|
#include "locking.h"
|
2007-06-12 18:35:45 +08:00
|
|
|
|
|
|
|
struct btrfs_iget_args {
|
|
|
|
u64 ino;
|
|
|
|
struct btrfs_root *root;
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct inode_operations btrfs_dir_inode_operations;
|
|
|
|
static struct inode_operations btrfs_symlink_inode_operations;
|
|
|
|
static struct inode_operations btrfs_dir_ro_inode_operations;
|
2007-07-11 22:18:17 +08:00
|
|
|
static struct inode_operations btrfs_special_inode_operations;
|
2007-06-12 18:35:45 +08:00
|
|
|
static struct inode_operations btrfs_file_inode_operations;
|
|
|
|
static struct address_space_operations btrfs_aops;
|
|
|
|
static struct address_space_operations btrfs_symlink_aops;
|
|
|
|
static struct file_operations btrfs_dir_file_operations;
|
2008-01-25 05:13:08 +08:00
|
|
|
static struct extent_io_ops btrfs_extent_io_ops;
|
2007-06-12 18:35:45 +08:00
|
|
|
|
|
|
|
static struct kmem_cache *btrfs_inode_cachep;
|
|
|
|
struct kmem_cache *btrfs_trans_handle_cachep;
|
|
|
|
struct kmem_cache *btrfs_transaction_cachep;
|
|
|
|
struct kmem_cache *btrfs_path_cachep;
|
|
|
|
|
|
|
|
#define S_SHIFT 12
|
|
|
|
static unsigned char btrfs_type_by_mode[S_IFMT >> S_SHIFT] = {
|
|
|
|
[S_IFREG >> S_SHIFT] = BTRFS_FT_REG_FILE,
|
|
|
|
[S_IFDIR >> S_SHIFT] = BTRFS_FT_DIR,
|
|
|
|
[S_IFCHR >> S_SHIFT] = BTRFS_FT_CHRDEV,
|
|
|
|
[S_IFBLK >> S_SHIFT] = BTRFS_FT_BLKDEV,
|
|
|
|
[S_IFIFO >> S_SHIFT] = BTRFS_FT_FIFO,
|
|
|
|
[S_IFSOCK >> S_SHIFT] = BTRFS_FT_SOCK,
|
|
|
|
[S_IFLNK >> S_SHIFT] = BTRFS_FT_SYMLINK,
|
|
|
|
};
|
|
|
|
|
2008-07-25 00:17:14 +08:00
|
|
|
static void btrfs_truncate(struct inode *inode);
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
static int btrfs_finish_ordered_io(struct inode *inode, u64 start, u64 end);
|
2008-11-07 11:02:51 +08:00
|
|
|
static noinline int cow_file_range(struct inode *inode,
|
|
|
|
struct page *locked_page,
|
|
|
|
u64 start, u64 end, int *page_started,
|
|
|
|
unsigned long *nr_written, int unlock);
|
2008-07-25 00:17:14 +08:00
|
|
|
|
2009-02-04 22:29:13 +08:00
|
|
|
static int btrfs_init_inode_security(struct inode *inode, struct inode *dir)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = btrfs_init_acl(inode, dir);
|
|
|
|
if (!err)
|
|
|
|
err = btrfs_xattr_security_init(inode, dir);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
/*
|
|
|
|
* this does all the hard work for inserting an inline extent into
|
|
|
|
* the btree. The caller should have done a btrfs_drop_extents so that
|
|
|
|
* no overlapping inline items exist in the btree
|
|
|
|
*/
|
2009-01-06 10:25:51 +08:00
|
|
|
static noinline int insert_inline_extent(struct btrfs_trans_handle *trans,
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
struct btrfs_root *root, struct inode *inode,
|
|
|
|
u64 start, size_t size, size_t compressed_size,
|
|
|
|
struct page **compressed_pages)
|
|
|
|
{
|
|
|
|
struct btrfs_key key;
|
|
|
|
struct btrfs_path *path;
|
|
|
|
struct extent_buffer *leaf;
|
|
|
|
struct page *page = NULL;
|
|
|
|
char *kaddr;
|
|
|
|
unsigned long ptr;
|
|
|
|
struct btrfs_file_extent_item *ei;
|
|
|
|
int err = 0;
|
|
|
|
int ret;
|
|
|
|
size_t cur_size = size;
|
|
|
|
size_t datasize;
|
|
|
|
unsigned long offset;
|
|
|
|
int use_compress = 0;
|
|
|
|
|
|
|
|
if (compressed_size && compressed_pages) {
|
|
|
|
use_compress = 1;
|
|
|
|
cur_size = compressed_size;
|
|
|
|
}
|
|
|
|
|
2009-01-06 10:25:51 +08:00
|
|
|
path = btrfs_alloc_path();
|
|
|
|
if (!path)
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
return -ENOMEM;
|
|
|
|
|
2009-03-13 23:00:37 +08:00
|
|
|
path->leave_spinning = 1;
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
btrfs_set_trans_block_group(trans, inode);
|
|
|
|
|
|
|
|
key.objectid = inode->i_ino;
|
|
|
|
key.offset = start;
|
|
|
|
btrfs_set_key_type(&key, BTRFS_EXTENT_DATA_KEY);
|
|
|
|
datasize = btrfs_file_extent_calc_inline_size(cur_size);
|
|
|
|
|
|
|
|
inode_add_bytes(inode, size);
|
|
|
|
ret = btrfs_insert_empty_item(trans, root, path, &key,
|
|
|
|
datasize);
|
|
|
|
BUG_ON(ret);
|
|
|
|
if (ret) {
|
|
|
|
err = ret;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
leaf = path->nodes[0];
|
|
|
|
ei = btrfs_item_ptr(leaf, path->slots[0],
|
|
|
|
struct btrfs_file_extent_item);
|
|
|
|
btrfs_set_file_extent_generation(leaf, ei, trans->transid);
|
|
|
|
btrfs_set_file_extent_type(leaf, ei, BTRFS_FILE_EXTENT_INLINE);
|
|
|
|
btrfs_set_file_extent_encryption(leaf, ei, 0);
|
|
|
|
btrfs_set_file_extent_other_encoding(leaf, ei, 0);
|
|
|
|
btrfs_set_file_extent_ram_bytes(leaf, ei, size);
|
|
|
|
ptr = btrfs_file_extent_inline_start(ei);
|
|
|
|
|
|
|
|
if (use_compress) {
|
|
|
|
struct page *cpage;
|
|
|
|
int i = 0;
|
2009-01-06 10:25:51 +08:00
|
|
|
while (compressed_size > 0) {
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
cpage = compressed_pages[i];
|
2008-11-11 22:34:41 +08:00
|
|
|
cur_size = min_t(unsigned long, compressed_size,
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
PAGE_CACHE_SIZE);
|
|
|
|
|
2009-03-13 23:00:37 +08:00
|
|
|
kaddr = kmap_atomic(cpage, KM_USER0);
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
write_extent_buffer(leaf, kaddr, ptr, cur_size);
|
2009-03-13 23:00:37 +08:00
|
|
|
kunmap_atomic(kaddr, KM_USER0);
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
|
|
|
|
i++;
|
|
|
|
ptr += cur_size;
|
|
|
|
compressed_size -= cur_size;
|
|
|
|
}
|
|
|
|
btrfs_set_file_extent_compression(leaf, ei,
|
|
|
|
BTRFS_COMPRESS_ZLIB);
|
|
|
|
} else {
|
|
|
|
page = find_get_page(inode->i_mapping,
|
|
|
|
start >> PAGE_CACHE_SHIFT);
|
|
|
|
btrfs_set_file_extent_compression(leaf, ei, 0);
|
|
|
|
kaddr = kmap_atomic(page, KM_USER0);
|
|
|
|
offset = start & (PAGE_CACHE_SIZE - 1);
|
|
|
|
write_extent_buffer(leaf, kaddr + offset, ptr, size);
|
|
|
|
kunmap_atomic(kaddr, KM_USER0);
|
|
|
|
page_cache_release(page);
|
|
|
|
}
|
|
|
|
btrfs_mark_buffer_dirty(leaf);
|
|
|
|
btrfs_free_path(path);
|
|
|
|
|
|
|
|
BTRFS_I(inode)->disk_i_size = inode->i_size;
|
|
|
|
btrfs_update_inode(trans, root, inode);
|
|
|
|
return 0;
|
|
|
|
fail:
|
|
|
|
btrfs_free_path(path);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* conditionally insert an inline extent into the file. This
|
|
|
|
* does the checks required to make sure the data is small enough
|
|
|
|
* to fit as an inline extent.
|
|
|
|
*/
|
2009-03-13 08:12:45 +08:00
|
|
|
static noinline int cow_file_range_inline(struct btrfs_trans_handle *trans,
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
struct btrfs_root *root,
|
|
|
|
struct inode *inode, u64 start, u64 end,
|
|
|
|
size_t compressed_size,
|
|
|
|
struct page **compressed_pages)
|
|
|
|
{
|
|
|
|
u64 isize = i_size_read(inode);
|
|
|
|
u64 actual_end = min(end + 1, isize);
|
|
|
|
u64 inline_len = actual_end - start;
|
|
|
|
u64 aligned_end = (end + root->sectorsize - 1) &
|
|
|
|
~((u64)root->sectorsize - 1);
|
|
|
|
u64 hint_byte;
|
|
|
|
u64 data_len = inline_len;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (compressed_size)
|
|
|
|
data_len = compressed_size;
|
|
|
|
|
|
|
|
if (start > 0 ||
|
2008-11-01 00:46:39 +08:00
|
|
|
actual_end >= PAGE_CACHE_SIZE ||
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
data_len >= BTRFS_MAX_INLINE_DATA_SIZE(root) ||
|
|
|
|
(!compressed_size &&
|
|
|
|
(actual_end & (root->sectorsize - 1)) == 0) ||
|
|
|
|
end + 1 < isize ||
|
|
|
|
data_len > root->fs_info->max_inline) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = btrfs_drop_extents(trans, root, inode, start,
|
2009-09-12 00:27:37 +08:00
|
|
|
aligned_end, aligned_end, start,
|
|
|
|
&hint_byte, 1);
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
BUG_ON(ret);
|
|
|
|
|
|
|
|
if (isize > actual_end)
|
|
|
|
inline_len = min_t(u64, isize, actual_end);
|
|
|
|
ret = insert_inline_extent(trans, root, inode, start,
|
|
|
|
inline_len, compressed_size,
|
|
|
|
compressed_pages);
|
|
|
|
BUG_ON(ret);
|
2009-09-12 00:27:37 +08:00
|
|
|
btrfs_drop_extent_cache(inode, start, aligned_end - 1, 0);
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-11-07 11:02:51 +08:00
|
|
|
struct async_extent {
|
|
|
|
u64 start;
|
|
|
|
u64 ram_size;
|
|
|
|
u64 compressed_size;
|
|
|
|
struct page **pages;
|
|
|
|
unsigned long nr_pages;
|
|
|
|
struct list_head list;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct async_cow {
|
|
|
|
struct inode *inode;
|
|
|
|
struct btrfs_root *root;
|
|
|
|
struct page *locked_page;
|
|
|
|
u64 start;
|
|
|
|
u64 end;
|
|
|
|
struct list_head extents;
|
|
|
|
struct btrfs_work work;
|
|
|
|
};
|
|
|
|
|
|
|
|
static noinline int add_async_extent(struct async_cow *cow,
|
|
|
|
u64 start, u64 ram_size,
|
|
|
|
u64 compressed_size,
|
|
|
|
struct page **pages,
|
|
|
|
unsigned long nr_pages)
|
|
|
|
{
|
|
|
|
struct async_extent *async_extent;
|
|
|
|
|
|
|
|
async_extent = kmalloc(sizeof(*async_extent), GFP_NOFS);
|
|
|
|
async_extent->start = start;
|
|
|
|
async_extent->ram_size = ram_size;
|
|
|
|
async_extent->compressed_size = compressed_size;
|
|
|
|
async_extent->pages = pages;
|
|
|
|
async_extent->nr_pages = nr_pages;
|
|
|
|
list_add_tail(&async_extent->list, &cow->extents);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-09-30 03:18:18 +08:00
|
|
|
/*
|
2008-11-07 11:02:51 +08:00
|
|
|
* we create compressed extents in two phases. The first
|
|
|
|
* phase compresses a range of pages that have already been
|
|
|
|
* locked (both pages and state bits are locked).
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
*
|
2008-11-07 11:02:51 +08:00
|
|
|
* This is done inside an ordered work queue, and the compression
|
|
|
|
* is spread across many cpus. The actual IO submission is step
|
|
|
|
* two, and the ordered work queue takes care of making sure that
|
|
|
|
* happens in the same order things were put onto the queue by
|
|
|
|
* writepages and friends.
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
*
|
2008-11-07 11:02:51 +08:00
|
|
|
* If this code finds it can't get good compression, it puts an
|
|
|
|
* entry onto the work queue to write the uncompressed bytes. This
|
|
|
|
* makes sure that both compressed inodes and uncompressed inodes
|
|
|
|
* are written in the same order that pdflush sent them down.
|
2008-09-30 03:18:18 +08:00
|
|
|
*/
|
2008-11-07 11:02:51 +08:00
|
|
|
static noinline int compress_file_range(struct inode *inode,
|
|
|
|
struct page *locked_page,
|
|
|
|
u64 start, u64 end,
|
|
|
|
struct async_cow *async_cow,
|
|
|
|
int *num_added)
|
2007-08-28 04:49:44 +08:00
|
|
|
{
|
|
|
|
struct btrfs_root *root = BTRFS_I(inode)->root;
|
|
|
|
struct btrfs_trans_handle *trans;
|
2007-10-16 04:15:53 +08:00
|
|
|
u64 num_bytes;
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
u64 orig_start;
|
|
|
|
u64 disk_num_bytes;
|
2007-10-16 04:15:53 +08:00
|
|
|
u64 blocksize = root->sectorsize;
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
u64 actual_end;
|
2008-12-16 00:44:56 +08:00
|
|
|
u64 isize = i_size_read(inode);
|
2008-07-18 00:53:50 +08:00
|
|
|
int ret = 0;
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
struct page **pages = NULL;
|
|
|
|
unsigned long nr_pages;
|
|
|
|
unsigned long nr_pages_ret = 0;
|
|
|
|
unsigned long total_compressed = 0;
|
|
|
|
unsigned long total_in = 0;
|
|
|
|
unsigned long max_compressed = 128 * 1024;
|
2008-11-07 11:02:51 +08:00
|
|
|
unsigned long max_uncompressed = 128 * 1024;
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
int i;
|
|
|
|
int will_compress;
|
2007-08-28 04:49:44 +08:00
|
|
|
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
orig_start = start;
|
|
|
|
|
2008-12-16 00:44:56 +08:00
|
|
|
actual_end = min_t(u64, isize, end + 1);
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
again:
|
|
|
|
will_compress = 0;
|
|
|
|
nr_pages = (end >> PAGE_CACHE_SHIFT) - (start >> PAGE_CACHE_SHIFT) + 1;
|
|
|
|
nr_pages = min(nr_pages, (128 * 1024UL) / PAGE_CACHE_SIZE);
|
2007-12-18 09:14:01 +08:00
|
|
|
|
2009-02-04 22:31:06 +08:00
|
|
|
/*
|
|
|
|
* we don't want to send crud past the end of i_size through
|
|
|
|
* compression, that's just a waste of CPU time. So, if the
|
|
|
|
* end of the file is before the start of our current
|
|
|
|
* requested range of bytes, we bail out to the uncompressed
|
|
|
|
* cleanup code that can deal with all of this.
|
|
|
|
*
|
|
|
|
* It isn't really the fastest way to fix things, but this is a
|
|
|
|
* very uncommon corner.
|
|
|
|
*/
|
|
|
|
if (actual_end <= start)
|
|
|
|
goto cleanup_and_bail_uncompressed;
|
|
|
|
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
total_compressed = actual_end - start;
|
|
|
|
|
|
|
|
/* we want to make sure that amount of ram required to uncompress
|
|
|
|
* an extent is reasonable, so we limit the total size in ram
|
2008-11-07 11:02:51 +08:00
|
|
|
* of a compressed extent to 128k. This is a crucial number
|
|
|
|
* because it also controls how easily we can spread reads across
|
|
|
|
* cpus for decompression.
|
|
|
|
*
|
|
|
|
* We also want to make sure the amount of IO required to do
|
|
|
|
* a random read is reasonably small, so we limit the size of
|
|
|
|
* a compressed extent to 128k.
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
*/
|
|
|
|
total_compressed = min(total_compressed, max_uncompressed);
|
2007-10-16 04:15:53 +08:00
|
|
|
num_bytes = (end - start + blocksize) & ~(blocksize - 1);
|
2007-12-18 09:14:01 +08:00
|
|
|
num_bytes = max(blocksize, num_bytes);
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
disk_num_bytes = num_bytes;
|
|
|
|
total_in = 0;
|
|
|
|
ret = 0;
|
2007-10-16 04:15:53 +08:00
|
|
|
|
2008-11-07 11:02:51 +08:00
|
|
|
/*
|
|
|
|
* we do compression for mount -o compress and when the
|
|
|
|
* inode has not been flagged as nocompress. This flag can
|
|
|
|
* change at any time if we discover bad compression ratios.
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
*/
|
2009-04-17 16:37:41 +08:00
|
|
|
if (!(BTRFS_I(inode)->flags & BTRFS_INODE_NOCOMPRESS) &&
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
btrfs_test_opt(root, COMPRESS)) {
|
|
|
|
WARN_ON(pages);
|
2008-10-31 01:22:14 +08:00
|
|
|
pages = kzalloc(sizeof(struct page *) * nr_pages, GFP_NOFS);
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
|
|
|
|
ret = btrfs_zlib_compress_pages(inode->i_mapping, start,
|
|
|
|
total_compressed, pages,
|
|
|
|
nr_pages, &nr_pages_ret,
|
|
|
|
&total_in,
|
|
|
|
&total_compressed,
|
|
|
|
max_compressed);
|
|
|
|
|
|
|
|
if (!ret) {
|
|
|
|
unsigned long offset = total_compressed &
|
|
|
|
(PAGE_CACHE_SIZE - 1);
|
|
|
|
struct page *page = pages[nr_pages_ret - 1];
|
|
|
|
char *kaddr;
|
|
|
|
|
|
|
|
/* zero the tail end of the last page, we might be
|
|
|
|
* sending it down to disk
|
|
|
|
*/
|
|
|
|
if (offset) {
|
|
|
|
kaddr = kmap_atomic(page, KM_USER0);
|
|
|
|
memset(kaddr + offset, 0,
|
|
|
|
PAGE_CACHE_SIZE - offset);
|
|
|
|
kunmap_atomic(kaddr, KM_USER0);
|
|
|
|
}
|
|
|
|
will_compress = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (start == 0) {
|
2008-11-07 11:02:51 +08:00
|
|
|
trans = btrfs_join_transaction(root, 1);
|
|
|
|
BUG_ON(!trans);
|
|
|
|
btrfs_set_trans_block_group(trans, inode);
|
|
|
|
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
/* lets try to make an inline extent */
|
2008-11-07 11:02:51 +08:00
|
|
|
if (ret || total_in < (actual_end - start)) {
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
/* we didn't compress the entire range, try
|
2008-11-07 11:02:51 +08:00
|
|
|
* to make an uncompressed inline extent.
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
*/
|
|
|
|
ret = cow_file_range_inline(trans, root, inode,
|
|
|
|
start, end, 0, NULL);
|
|
|
|
} else {
|
2008-11-07 11:02:51 +08:00
|
|
|
/* try making a compressed inline extent */
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
ret = cow_file_range_inline(trans, root, inode,
|
|
|
|
start, end,
|
|
|
|
total_compressed, pages);
|
|
|
|
}
|
2008-11-07 11:02:51 +08:00
|
|
|
btrfs_end_transaction(trans, root);
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
if (ret == 0) {
|
2008-11-07 11:02:51 +08:00
|
|
|
/*
|
|
|
|
* inline extent creation worked, we don't need
|
|
|
|
* to create any more async work items. Unlock
|
|
|
|
* and free up our temp pages.
|
|
|
|
*/
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
extent_clear_unlock_delalloc(inode,
|
|
|
|
&BTRFS_I(inode)->io_tree,
|
2008-11-07 11:02:51 +08:00
|
|
|
start, end, NULL, 1, 0,
|
2009-09-03 04:53:46 +08:00
|
|
|
0, 1, 1, 1, 0);
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
ret = 0;
|
|
|
|
goto free_pages_out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (will_compress) {
|
|
|
|
/*
|
|
|
|
* we aren't doing an inline extent round the compressed size
|
|
|
|
* up to a block size boundary so the allocator does sane
|
|
|
|
* things
|
|
|
|
*/
|
|
|
|
total_compressed = (total_compressed + blocksize - 1) &
|
|
|
|
~(blocksize - 1);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* one last check to make sure the compression is really a
|
|
|
|
* win, compare the page count read with the blocks on disk
|
|
|
|
*/
|
|
|
|
total_in = (total_in + PAGE_CACHE_SIZE - 1) &
|
|
|
|
~(PAGE_CACHE_SIZE - 1);
|
|
|
|
if (total_compressed >= total_in) {
|
|
|
|
will_compress = 0;
|
|
|
|
} else {
|
|
|
|
disk_num_bytes = total_compressed;
|
|
|
|
num_bytes = total_in;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!will_compress && pages) {
|
|
|
|
/*
|
|
|
|
* the compression code ran but failed to make things smaller,
|
|
|
|
* free any pages it allocated and our page pointer array
|
|
|
|
*/
|
|
|
|
for (i = 0; i < nr_pages_ret; i++) {
|
2008-11-01 00:46:39 +08:00
|
|
|
WARN_ON(pages[i]->mapping);
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
page_cache_release(pages[i]);
|
|
|
|
}
|
|
|
|
kfree(pages);
|
|
|
|
pages = NULL;
|
|
|
|
total_compressed = 0;
|
|
|
|
nr_pages_ret = 0;
|
|
|
|
|
|
|
|
/* flag the file so we don't compress in the future */
|
2009-04-17 16:37:41 +08:00
|
|
|
BTRFS_I(inode)->flags |= BTRFS_INODE_NOCOMPRESS;
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
}
|
2008-11-07 11:02:51 +08:00
|
|
|
if (will_compress) {
|
|
|
|
*num_added += 1;
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
|
2008-11-07 11:02:51 +08:00
|
|
|
/* the async work queues will take care of doing actual
|
|
|
|
* allocation on disk for these compressed pages,
|
|
|
|
* and will submit them to the elevator.
|
|
|
|
*/
|
|
|
|
add_async_extent(async_cow, start, num_bytes,
|
|
|
|
total_compressed, pages, nr_pages_ret);
|
2007-11-01 23:28:41 +08:00
|
|
|
|
2008-12-16 00:44:56 +08:00
|
|
|
if (start + num_bytes < end && start + num_bytes < actual_end) {
|
2008-11-07 11:02:51 +08:00
|
|
|
start += num_bytes;
|
|
|
|
pages = NULL;
|
|
|
|
cond_resched();
|
|
|
|
goto again;
|
|
|
|
}
|
|
|
|
} else {
|
2009-02-04 22:31:06 +08:00
|
|
|
cleanup_and_bail_uncompressed:
|
2008-11-07 11:02:51 +08:00
|
|
|
/*
|
|
|
|
* No compression, but we still need to write the pages in
|
|
|
|
* the file we've been given so far. redirty the locked
|
|
|
|
* page if it corresponds to our extent and set things up
|
|
|
|
* for the async work queue to run cow_file_range to do
|
|
|
|
* the normal delalloc dance
|
|
|
|
*/
|
|
|
|
if (page_offset(locked_page) >= start &&
|
|
|
|
page_offset(locked_page) <= end) {
|
|
|
|
__set_page_dirty_nobuffers(locked_page);
|
|
|
|
/* unlocked later on in the async handlers */
|
|
|
|
}
|
|
|
|
add_async_extent(async_cow, start, end - start + 1, 0, NULL, 0);
|
|
|
|
*num_added += 1;
|
|
|
|
}
|
2008-04-17 23:29:12 +08:00
|
|
|
|
2008-11-07 11:02:51 +08:00
|
|
|
out:
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
free_pages_out:
|
|
|
|
for (i = 0; i < nr_pages_ret; i++) {
|
|
|
|
WARN_ON(pages[i]->mapping);
|
|
|
|
page_cache_release(pages[i]);
|
|
|
|
}
|
2009-01-06 10:25:51 +08:00
|
|
|
kfree(pages);
|
2008-11-07 11:02:51 +08:00
|
|
|
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* phase two of compressed writeback. This is the ordered portion
|
|
|
|
* of the code, which only gets called in the order the work was
|
|
|
|
* queued. We walk all the async extents created by compress_file_range
|
|
|
|
* and send them down to the disk.
|
|
|
|
*/
|
|
|
|
static noinline int submit_compressed_extents(struct inode *inode,
|
|
|
|
struct async_cow *async_cow)
|
|
|
|
{
|
|
|
|
struct async_extent *async_extent;
|
|
|
|
u64 alloc_hint = 0;
|
|
|
|
struct btrfs_trans_handle *trans;
|
|
|
|
struct btrfs_key ins;
|
|
|
|
struct extent_map *em;
|
|
|
|
struct btrfs_root *root = BTRFS_I(inode)->root;
|
|
|
|
struct extent_map_tree *em_tree = &BTRFS_I(inode)->extent_tree;
|
|
|
|
struct extent_io_tree *io_tree;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (list_empty(&async_cow->extents))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
trans = btrfs_join_transaction(root, 1);
|
|
|
|
|
2009-01-06 10:25:51 +08:00
|
|
|
while (!list_empty(&async_cow->extents)) {
|
2008-11-07 11:02:51 +08:00
|
|
|
async_extent = list_entry(async_cow->extents.next,
|
|
|
|
struct async_extent, list);
|
|
|
|
list_del(&async_extent->list);
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
|
2008-11-07 11:02:51 +08:00
|
|
|
io_tree = &BTRFS_I(inode)->io_tree;
|
|
|
|
|
|
|
|
/* did the compression code fall back to uncompressed IO? */
|
|
|
|
if (!async_extent->pages) {
|
|
|
|
int page_started = 0;
|
|
|
|
unsigned long nr_written = 0;
|
|
|
|
|
|
|
|
lock_extent(io_tree, async_extent->start,
|
2009-01-06 10:25:51 +08:00
|
|
|
async_extent->start +
|
|
|
|
async_extent->ram_size - 1, GFP_NOFS);
|
2008-11-07 11:02:51 +08:00
|
|
|
|
|
|
|
/* allocate blocks */
|
|
|
|
cow_file_range(inode, async_cow->locked_page,
|
|
|
|
async_extent->start,
|
|
|
|
async_extent->start +
|
|
|
|
async_extent->ram_size - 1,
|
|
|
|
&page_started, &nr_written, 0);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* if page_started, cow_file_range inserted an
|
|
|
|
* inline extent and took care of all the unlocking
|
|
|
|
* and IO for us. Otherwise, we need to submit
|
|
|
|
* all those pages down to the drive.
|
|
|
|
*/
|
|
|
|
if (!page_started)
|
|
|
|
extent_write_locked_range(io_tree,
|
|
|
|
inode, async_extent->start,
|
2009-01-06 10:25:51 +08:00
|
|
|
async_extent->start +
|
2008-11-07 11:02:51 +08:00
|
|
|
async_extent->ram_size - 1,
|
|
|
|
btrfs_get_extent,
|
|
|
|
WB_SYNC_ALL);
|
|
|
|
kfree(async_extent);
|
|
|
|
cond_resched();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
lock_extent(io_tree, async_extent->start,
|
|
|
|
async_extent->start + async_extent->ram_size - 1,
|
|
|
|
GFP_NOFS);
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
/*
|
2008-11-07 11:02:51 +08:00
|
|
|
* here we're doing allocation and writeback of the
|
|
|
|
* compressed pages
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
*/
|
2008-11-07 11:02:51 +08:00
|
|
|
btrfs_drop_extent_cache(inode, async_extent->start,
|
|
|
|
async_extent->start +
|
|
|
|
async_extent->ram_size - 1, 0);
|
|
|
|
|
|
|
|
ret = btrfs_reserve_extent(trans, root,
|
|
|
|
async_extent->compressed_size,
|
|
|
|
async_extent->compressed_size,
|
|
|
|
0, alloc_hint,
|
|
|
|
(u64)-1, &ins, 1);
|
|
|
|
BUG_ON(ret);
|
|
|
|
em = alloc_extent_map(GFP_NOFS);
|
|
|
|
em->start = async_extent->start;
|
|
|
|
em->len = async_extent->ram_size;
|
2008-11-11 00:53:33 +08:00
|
|
|
em->orig_start = em->start;
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
|
2008-11-07 11:02:51 +08:00
|
|
|
em->block_start = ins.objectid;
|
|
|
|
em->block_len = ins.offset;
|
|
|
|
em->bdev = root->fs_info->fs_devices->latest_bdev;
|
|
|
|
set_bit(EXTENT_FLAG_PINNED, &em->flags);
|
|
|
|
set_bit(EXTENT_FLAG_COMPRESSED, &em->flags);
|
|
|
|
|
2009-01-06 10:25:51 +08:00
|
|
|
while (1) {
|
2009-09-03 04:24:52 +08:00
|
|
|
write_lock(&em_tree->lock);
|
2008-11-07 11:02:51 +08:00
|
|
|
ret = add_extent_mapping(em_tree, em);
|
2009-09-03 04:24:52 +08:00
|
|
|
write_unlock(&em_tree->lock);
|
2008-11-07 11:02:51 +08:00
|
|
|
if (ret != -EEXIST) {
|
|
|
|
free_extent_map(em);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
btrfs_drop_extent_cache(inode, async_extent->start,
|
|
|
|
async_extent->start +
|
|
|
|
async_extent->ram_size - 1, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = btrfs_add_ordered_extent(inode, async_extent->start,
|
|
|
|
ins.objectid,
|
|
|
|
async_extent->ram_size,
|
|
|
|
ins.offset,
|
|
|
|
BTRFS_ORDERED_COMPRESSED);
|
|
|
|
BUG_ON(ret);
|
|
|
|
|
|
|
|
btrfs_end_transaction(trans, root);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* clear dirty, set writeback and unlock the pages.
|
|
|
|
*/
|
|
|
|
extent_clear_unlock_delalloc(inode,
|
|
|
|
&BTRFS_I(inode)->io_tree,
|
|
|
|
async_extent->start,
|
|
|
|
async_extent->start +
|
|
|
|
async_extent->ram_size - 1,
|
2009-09-03 04:53:46 +08:00
|
|
|
NULL, 1, 1, 0, 1, 1, 0, 0);
|
2008-11-07 11:02:51 +08:00
|
|
|
|
|
|
|
ret = btrfs_submit_compressed_write(inode,
|
2009-01-06 10:25:51 +08:00
|
|
|
async_extent->start,
|
|
|
|
async_extent->ram_size,
|
|
|
|
ins.objectid,
|
|
|
|
ins.offset, async_extent->pages,
|
|
|
|
async_extent->nr_pages);
|
2008-11-07 11:02:51 +08:00
|
|
|
|
|
|
|
BUG_ON(ret);
|
|
|
|
trans = btrfs_join_transaction(root, 1);
|
|
|
|
alloc_hint = ins.objectid + ins.offset;
|
|
|
|
kfree(async_extent);
|
|
|
|
cond_resched();
|
|
|
|
}
|
|
|
|
|
|
|
|
btrfs_end_transaction(trans, root);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* when extent_io.c finds a delayed allocation range in the file,
|
|
|
|
* the call backs end up in this code. The basic idea is to
|
|
|
|
* allocate extents on disk for the range, and create ordered data structs
|
|
|
|
* in ram to track those extents.
|
|
|
|
*
|
|
|
|
* locked_page is the page that writepage had locked already. We use
|
|
|
|
* it to make sure we don't do extra locks or unlocks.
|
|
|
|
*
|
|
|
|
* *page_started is set to one if we unlock locked_page and do everything
|
|
|
|
* required to start IO on it. It may be clean and already done with
|
|
|
|
* IO when we return.
|
|
|
|
*/
|
|
|
|
static noinline int cow_file_range(struct inode *inode,
|
|
|
|
struct page *locked_page,
|
|
|
|
u64 start, u64 end, int *page_started,
|
|
|
|
unsigned long *nr_written,
|
|
|
|
int unlock)
|
|
|
|
{
|
|
|
|
struct btrfs_root *root = BTRFS_I(inode)->root;
|
|
|
|
struct btrfs_trans_handle *trans;
|
|
|
|
u64 alloc_hint = 0;
|
|
|
|
u64 num_bytes;
|
|
|
|
unsigned long ram_size;
|
|
|
|
u64 disk_num_bytes;
|
|
|
|
u64 cur_alloc_size;
|
|
|
|
u64 blocksize = root->sectorsize;
|
|
|
|
u64 actual_end;
|
2008-12-16 00:44:56 +08:00
|
|
|
u64 isize = i_size_read(inode);
|
2008-11-07 11:02:51 +08:00
|
|
|
struct btrfs_key ins;
|
|
|
|
struct extent_map *em;
|
|
|
|
struct extent_map_tree *em_tree = &BTRFS_I(inode)->extent_tree;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
trans = btrfs_join_transaction(root, 1);
|
|
|
|
BUG_ON(!trans);
|
|
|
|
btrfs_set_trans_block_group(trans, inode);
|
|
|
|
|
2008-12-16 00:44:56 +08:00
|
|
|
actual_end = min_t(u64, isize, end + 1);
|
2008-11-07 11:02:51 +08:00
|
|
|
|
|
|
|
num_bytes = (end - start + blocksize) & ~(blocksize - 1);
|
|
|
|
num_bytes = max(blocksize, num_bytes);
|
|
|
|
disk_num_bytes = num_bytes;
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
if (start == 0) {
|
|
|
|
/* lets try to make an inline extent */
|
|
|
|
ret = cow_file_range_inline(trans, root, inode,
|
|
|
|
start, end, 0, NULL);
|
|
|
|
if (ret == 0) {
|
|
|
|
extent_clear_unlock_delalloc(inode,
|
|
|
|
&BTRFS_I(inode)->io_tree,
|
|
|
|
start, end, NULL, 1, 1,
|
2009-09-03 04:53:46 +08:00
|
|
|
1, 1, 1, 1, 0);
|
2008-11-07 11:02:51 +08:00
|
|
|
*nr_written = *nr_written +
|
|
|
|
(end - start + PAGE_CACHE_SIZE) / PAGE_CACHE_SIZE;
|
|
|
|
*page_started = 1;
|
|
|
|
ret = 0;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
BUG_ON(disk_num_bytes >
|
|
|
|
btrfs_super_total_bytes(&root->fs_info->super_copy));
|
|
|
|
|
2009-09-19 04:07:03 +08:00
|
|
|
|
|
|
|
read_lock(&BTRFS_I(inode)->extent_tree.lock);
|
|
|
|
em = search_extent_mapping(&BTRFS_I(inode)->extent_tree,
|
|
|
|
start, num_bytes);
|
|
|
|
if (em) {
|
|
|
|
alloc_hint = em->block_start;
|
|
|
|
free_extent_map(em);
|
|
|
|
}
|
|
|
|
read_unlock(&BTRFS_I(inode)->extent_tree.lock);
|
2008-11-07 11:02:51 +08:00
|
|
|
btrfs_drop_extent_cache(inode, start, start + num_bytes - 1, 0);
|
|
|
|
|
2009-01-06 10:25:51 +08:00
|
|
|
while (disk_num_bytes > 0) {
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
cur_alloc_size = min(disk_num_bytes, root->fs_info->max_extent);
|
2008-07-18 00:53:50 +08:00
|
|
|
ret = btrfs_reserve_extent(trans, root, cur_alloc_size,
|
2008-11-07 11:02:51 +08:00
|
|
|
root->sectorsize, 0, alloc_hint,
|
2008-07-18 00:53:50 +08:00
|
|
|
(u64)-1, &ins, 1);
|
2009-01-06 10:25:51 +08:00
|
|
|
BUG_ON(ret);
|
|
|
|
|
2008-07-18 00:53:50 +08:00
|
|
|
em = alloc_extent_map(GFP_NOFS);
|
|
|
|
em->start = start;
|
2008-11-11 00:53:33 +08:00
|
|
|
em->orig_start = em->start;
|
2008-11-07 11:02:51 +08:00
|
|
|
ram_size = ins.offset;
|
|
|
|
em->len = ins.offset;
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
|
2008-07-18 00:53:50 +08:00
|
|
|
em->block_start = ins.objectid;
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
em->block_len = ins.offset;
|
2008-07-18 00:53:50 +08:00
|
|
|
em->bdev = root->fs_info->fs_devices->latest_bdev;
|
2008-07-19 00:01:11 +08:00
|
|
|
set_bit(EXTENT_FLAG_PINNED, &em->flags);
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
|
2009-01-06 10:25:51 +08:00
|
|
|
while (1) {
|
2009-09-03 04:24:52 +08:00
|
|
|
write_lock(&em_tree->lock);
|
2008-07-18 00:53:50 +08:00
|
|
|
ret = add_extent_mapping(em_tree, em);
|
2009-09-03 04:24:52 +08:00
|
|
|
write_unlock(&em_tree->lock);
|
2008-07-18 00:53:50 +08:00
|
|
|
if (ret != -EEXIST) {
|
|
|
|
free_extent_map(em);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
btrfs_drop_extent_cache(inode, start,
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
start + ram_size - 1, 0);
|
2008-07-18 00:53:50 +08:00
|
|
|
}
|
|
|
|
|
2008-04-14 21:46:10 +08:00
|
|
|
cur_alloc_size = ins.offset;
|
2008-07-18 00:53:50 +08:00
|
|
|
ret = btrfs_add_ordered_extent(inode, start, ins.objectid,
|
2008-11-07 11:02:51 +08:00
|
|
|
ram_size, cur_alloc_size, 0);
|
2008-07-18 00:53:50 +08:00
|
|
|
BUG_ON(ret);
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
|
2008-12-12 23:03:38 +08:00
|
|
|
if (root->root_key.objectid ==
|
|
|
|
BTRFS_DATA_RELOC_TREE_OBJECTID) {
|
|
|
|
ret = btrfs_reloc_clone_csums(inode, start,
|
|
|
|
cur_alloc_size);
|
|
|
|
BUG_ON(ret);
|
|
|
|
}
|
|
|
|
|
2009-01-06 10:25:51 +08:00
|
|
|
if (disk_num_bytes < cur_alloc_size)
|
2008-04-17 23:29:12 +08:00
|
|
|
break;
|
2009-01-06 10:25:51 +08:00
|
|
|
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
/* we're not doing compressed IO, don't unlock the first
|
|
|
|
* page (which the caller expects to stay locked), don't
|
|
|
|
* clear any dirty bits and don't set any writeback bits
|
2009-09-03 04:53:46 +08:00
|
|
|
*
|
|
|
|
* Do set the Private2 bit so we know this page was properly
|
|
|
|
* setup for writepage
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
*/
|
|
|
|
extent_clear_unlock_delalloc(inode, &BTRFS_I(inode)->io_tree,
|
|
|
|
start, start + ram_size - 1,
|
2008-11-07 11:02:51 +08:00
|
|
|
locked_page, unlock, 1,
|
2009-09-03 04:53:46 +08:00
|
|
|
1, 0, 0, 0, 1);
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
disk_num_bytes -= cur_alloc_size;
|
2007-12-18 09:14:04 +08:00
|
|
|
num_bytes -= cur_alloc_size;
|
|
|
|
alloc_hint = ins.objectid + ins.offset;
|
|
|
|
start += cur_alloc_size;
|
2007-08-28 04:49:44 +08:00
|
|
|
}
|
|
|
|
out:
|
2008-11-07 11:02:51 +08:00
|
|
|
ret = 0;
|
2007-08-28 04:49:44 +08:00
|
|
|
btrfs_end_transaction(trans, root);
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
|
2007-12-18 09:14:01 +08:00
|
|
|
return ret;
|
2008-11-07 11:02:51 +08:00
|
|
|
}
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
|
2008-11-07 11:02:51 +08:00
|
|
|
/*
|
|
|
|
* work queue call back to started compression on a file and pages
|
|
|
|
*/
|
|
|
|
static noinline void async_cow_start(struct btrfs_work *work)
|
|
|
|
{
|
|
|
|
struct async_cow *async_cow;
|
|
|
|
int num_added = 0;
|
|
|
|
async_cow = container_of(work, struct async_cow, work);
|
|
|
|
|
|
|
|
compress_file_range(async_cow->inode, async_cow->locked_page,
|
|
|
|
async_cow->start, async_cow->end, async_cow,
|
|
|
|
&num_added);
|
|
|
|
if (num_added == 0)
|
|
|
|
async_cow->inode = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* work queue call back to submit previously compressed pages
|
|
|
|
*/
|
|
|
|
static noinline void async_cow_submit(struct btrfs_work *work)
|
|
|
|
{
|
|
|
|
struct async_cow *async_cow;
|
|
|
|
struct btrfs_root *root;
|
|
|
|
unsigned long nr_pages;
|
|
|
|
|
|
|
|
async_cow = container_of(work, struct async_cow, work);
|
|
|
|
|
|
|
|
root = async_cow->root;
|
|
|
|
nr_pages = (async_cow->end - async_cow->start + PAGE_CACHE_SIZE) >>
|
|
|
|
PAGE_CACHE_SHIFT;
|
|
|
|
|
|
|
|
atomic_sub(nr_pages, &root->fs_info->async_delalloc_pages);
|
|
|
|
|
|
|
|
if (atomic_read(&root->fs_info->async_delalloc_pages) <
|
|
|
|
5 * 1042 * 1024 &&
|
|
|
|
waitqueue_active(&root->fs_info->async_submit_wait))
|
|
|
|
wake_up(&root->fs_info->async_submit_wait);
|
|
|
|
|
2009-01-06 10:25:51 +08:00
|
|
|
if (async_cow->inode)
|
2008-11-07 11:02:51 +08:00
|
|
|
submit_compressed_extents(async_cow->inode, async_cow);
|
|
|
|
}
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
|
2008-11-07 11:02:51 +08:00
|
|
|
static noinline void async_cow_free(struct btrfs_work *work)
|
|
|
|
{
|
|
|
|
struct async_cow *async_cow;
|
|
|
|
async_cow = container_of(work, struct async_cow, work);
|
|
|
|
kfree(async_cow);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cow_file_range_async(struct inode *inode, struct page *locked_page,
|
|
|
|
u64 start, u64 end, int *page_started,
|
|
|
|
unsigned long *nr_written)
|
|
|
|
{
|
|
|
|
struct async_cow *async_cow;
|
|
|
|
struct btrfs_root *root = BTRFS_I(inode)->root;
|
|
|
|
unsigned long nr_pages;
|
|
|
|
u64 cur_end;
|
|
|
|
int limit = 10 * 1024 * 1042;
|
|
|
|
|
|
|
|
clear_extent_bit(&BTRFS_I(inode)->io_tree, start, end, EXTENT_LOCKED |
|
2009-09-03 03:04:12 +08:00
|
|
|
EXTENT_DELALLOC, 1, 0, NULL, GFP_NOFS);
|
2009-01-06 10:25:51 +08:00
|
|
|
while (start < end) {
|
2008-11-07 11:02:51 +08:00
|
|
|
async_cow = kmalloc(sizeof(*async_cow), GFP_NOFS);
|
|
|
|
async_cow->inode = inode;
|
|
|
|
async_cow->root = root;
|
|
|
|
async_cow->locked_page = locked_page;
|
|
|
|
async_cow->start = start;
|
|
|
|
|
2009-04-17 16:37:41 +08:00
|
|
|
if (BTRFS_I(inode)->flags & BTRFS_INODE_NOCOMPRESS)
|
2008-11-07 11:02:51 +08:00
|
|
|
cur_end = end;
|
|
|
|
else
|
|
|
|
cur_end = min(end, start + 512 * 1024 - 1);
|
|
|
|
|
|
|
|
async_cow->end = cur_end;
|
|
|
|
INIT_LIST_HEAD(&async_cow->extents);
|
|
|
|
|
|
|
|
async_cow->work.func = async_cow_start;
|
|
|
|
async_cow->work.ordered_func = async_cow_submit;
|
|
|
|
async_cow->work.ordered_free = async_cow_free;
|
|
|
|
async_cow->work.flags = 0;
|
|
|
|
|
|
|
|
nr_pages = (cur_end - start + PAGE_CACHE_SIZE) >>
|
|
|
|
PAGE_CACHE_SHIFT;
|
|
|
|
atomic_add(nr_pages, &root->fs_info->async_delalloc_pages);
|
|
|
|
|
|
|
|
btrfs_queue_worker(&root->fs_info->delalloc_workers,
|
|
|
|
&async_cow->work);
|
|
|
|
|
|
|
|
if (atomic_read(&root->fs_info->async_delalloc_pages) > limit) {
|
|
|
|
wait_event(root->fs_info->async_submit_wait,
|
|
|
|
(atomic_read(&root->fs_info->async_delalloc_pages) <
|
|
|
|
limit));
|
|
|
|
}
|
|
|
|
|
2009-01-06 10:25:51 +08:00
|
|
|
while (atomic_read(&root->fs_info->async_submit_draining) &&
|
2008-11-07 11:02:51 +08:00
|
|
|
atomic_read(&root->fs_info->async_delalloc_pages)) {
|
|
|
|
wait_event(root->fs_info->async_submit_wait,
|
|
|
|
(atomic_read(&root->fs_info->async_delalloc_pages) ==
|
|
|
|
0));
|
|
|
|
}
|
|
|
|
|
|
|
|
*nr_written += nr_pages;
|
|
|
|
start = cur_end + 1;
|
|
|
|
}
|
|
|
|
*page_started = 1;
|
|
|
|
return 0;
|
2007-12-18 09:14:01 +08:00
|
|
|
}
|
|
|
|
|
2009-01-06 10:25:51 +08:00
|
|
|
static noinline int csum_exist_in_range(struct btrfs_root *root,
|
2008-12-12 23:03:38 +08:00
|
|
|
u64 bytenr, u64 num_bytes)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
struct btrfs_ordered_sum *sums;
|
|
|
|
LIST_HEAD(list);
|
|
|
|
|
2009-01-07 00:42:00 +08:00
|
|
|
ret = btrfs_lookup_csums_range(root->fs_info->csum_root, bytenr,
|
|
|
|
bytenr + num_bytes - 1, &list);
|
2008-12-12 23:03:38 +08:00
|
|
|
if (ret == 0 && list_empty(&list))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
while (!list_empty(&list)) {
|
|
|
|
sums = list_entry(list.next, struct btrfs_ordered_sum, list);
|
|
|
|
list_del(&sums->list);
|
|
|
|
kfree(sums);
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2008-09-30 03:18:18 +08:00
|
|
|
/*
|
|
|
|
* when nowcow writeback call back. This checks for snapshots or COW copies
|
|
|
|
* of the extents that exist in the file, and COWs the file as required.
|
|
|
|
*
|
|
|
|
* If no cow copies or snapshots exist, we write directly to the existing
|
|
|
|
* blocks on disk
|
|
|
|
*/
|
2009-03-13 08:12:45 +08:00
|
|
|
static noinline int run_delalloc_nocow(struct inode *inode,
|
|
|
|
struct page *locked_page,
|
2008-11-07 11:02:51 +08:00
|
|
|
u64 start, u64 end, int *page_started, int force,
|
|
|
|
unsigned long *nr_written)
|
2007-12-18 09:14:01 +08:00
|
|
|
{
|
|
|
|
struct btrfs_root *root = BTRFS_I(inode)->root;
|
2008-08-06 01:05:02 +08:00
|
|
|
struct btrfs_trans_handle *trans;
|
2007-12-18 09:14:01 +08:00
|
|
|
struct extent_buffer *leaf;
|
|
|
|
struct btrfs_path *path;
|
2008-10-31 02:20:02 +08:00
|
|
|
struct btrfs_file_extent_item *fi;
|
2007-12-18 09:14:01 +08:00
|
|
|
struct btrfs_key found_key;
|
2008-10-31 02:20:02 +08:00
|
|
|
u64 cow_start;
|
|
|
|
u64 cur_offset;
|
|
|
|
u64 extent_end;
|
Btrfs: Mixed back reference (FORWARD ROLLING FORMAT CHANGE)
This commit introduces a new kind of back reference for btrfs metadata.
Once a filesystem has been mounted with this commit, IT WILL NO LONGER
BE MOUNTABLE BY OLDER KERNELS.
When a tree block in subvolume tree is cow'd, the reference counts of all
extents it points to are increased by one. At transaction commit time,
the old root of the subvolume is recorded in a "dead root" data structure,
and the btree it points to is later walked, dropping reference counts
and freeing any blocks where the reference count goes to 0.
The increments done during cow and decrements done after commit cancel out,
and the walk is a very expensive way to go about freeing the blocks that
are no longer referenced by the new btree root. This commit reduces the
transaction overhead by avoiding the need for dead root records.
When a non-shared tree block is cow'd, we free the old block at once, and the
new block inherits old block's references. When a tree block with reference
count > 1 is cow'd, we increase the reference counts of all extents
the new block points to by one, and decrease the old block's reference count by
one.
This dead tree avoidance code removes the need to modify the reference
counts of lower level extents when a non-shared tree block is cow'd.
But we still need to update back ref for all pointers in the block.
This is because the location of the block is recorded in the back ref
item.
We can solve this by introducing a new type of back ref. The new
back ref provides information about pointer's key, level and in which
tree the pointer lives. This information allow us to find the pointer
by searching the tree. The shortcoming of the new back ref is that it
only works for pointers in tree blocks referenced by their owner trees.
This is mostly a problem for snapshots, where resolving one of these
fuzzy back references would be O(number_of_snapshots) and quite slow.
The solution used here is to use the fuzzy back references in the common
case where a given tree block is only referenced by one root,
and use the full back references when multiple roots have a reference
on a given block.
This commit adds per subvolume red-black tree to keep trace of cached
inodes. The red-black tree helps the balancing code to find cached
inodes whose inode numbers within a given range.
This commit improves the balancing code by introducing several data
structures to keep the state of balancing. The most important one
is the back ref cache. It caches how the upper level tree blocks are
referenced. This greatly reduce the overhead of checking back ref.
The improved balancing code scales significantly better with a large
number of snapshots.
This is a very large commit and was written in a number of
pieces. But, they depend heavily on the disk format change and were
squashed together to make sure git bisect didn't end up in a
bad state wrt space balancing or the format change.
Signed-off-by: Yan Zheng <zheng.yan@oracle.com>
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2009-06-10 22:45:14 +08:00
|
|
|
u64 extent_offset;
|
2008-10-31 02:20:02 +08:00
|
|
|
u64 disk_bytenr;
|
|
|
|
u64 num_bytes;
|
|
|
|
int extent_type;
|
|
|
|
int ret;
|
2008-10-31 02:25:28 +08:00
|
|
|
int type;
|
2008-10-31 02:20:02 +08:00
|
|
|
int nocow;
|
|
|
|
int check_prev = 1;
|
2007-12-18 09:14:01 +08:00
|
|
|
|
|
|
|
path = btrfs_alloc_path();
|
|
|
|
BUG_ON(!path);
|
2008-08-06 01:05:02 +08:00
|
|
|
trans = btrfs_join_transaction(root, 1);
|
|
|
|
BUG_ON(!trans);
|
2007-12-18 09:14:01 +08:00
|
|
|
|
2008-10-31 02:20:02 +08:00
|
|
|
cow_start = (u64)-1;
|
|
|
|
cur_offset = start;
|
|
|
|
while (1) {
|
|
|
|
ret = btrfs_lookup_file_extent(trans, root, path, inode->i_ino,
|
|
|
|
cur_offset, 0);
|
|
|
|
BUG_ON(ret < 0);
|
|
|
|
if (ret > 0 && path->slots[0] > 0 && check_prev) {
|
|
|
|
leaf = path->nodes[0];
|
|
|
|
btrfs_item_key_to_cpu(leaf, &found_key,
|
|
|
|
path->slots[0] - 1);
|
|
|
|
if (found_key.objectid == inode->i_ino &&
|
|
|
|
found_key.type == BTRFS_EXTENT_DATA_KEY)
|
|
|
|
path->slots[0]--;
|
|
|
|
}
|
|
|
|
check_prev = 0;
|
|
|
|
next_slot:
|
|
|
|
leaf = path->nodes[0];
|
|
|
|
if (path->slots[0] >= btrfs_header_nritems(leaf)) {
|
|
|
|
ret = btrfs_next_leaf(root, path);
|
|
|
|
if (ret < 0)
|
|
|
|
BUG_ON(1);
|
|
|
|
if (ret > 0)
|
|
|
|
break;
|
|
|
|
leaf = path->nodes[0];
|
|
|
|
}
|
2007-12-18 09:14:01 +08:00
|
|
|
|
2008-10-31 02:20:02 +08:00
|
|
|
nocow = 0;
|
|
|
|
disk_bytenr = 0;
|
2008-12-12 23:03:38 +08:00
|
|
|
num_bytes = 0;
|
2008-10-31 02:20:02 +08:00
|
|
|
btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
|
|
|
|
|
|
|
|
if (found_key.objectid > inode->i_ino ||
|
|
|
|
found_key.type > BTRFS_EXTENT_DATA_KEY ||
|
|
|
|
found_key.offset > end)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (found_key.offset > cur_offset) {
|
|
|
|
extent_end = found_key.offset;
|
|
|
|
goto out_check;
|
|
|
|
}
|
|
|
|
|
|
|
|
fi = btrfs_item_ptr(leaf, path->slots[0],
|
|
|
|
struct btrfs_file_extent_item);
|
|
|
|
extent_type = btrfs_file_extent_type(leaf, fi);
|
|
|
|
|
2008-10-31 02:25:28 +08:00
|
|
|
if (extent_type == BTRFS_FILE_EXTENT_REG ||
|
|
|
|
extent_type == BTRFS_FILE_EXTENT_PREALLOC) {
|
2008-10-31 02:20:02 +08:00
|
|
|
disk_bytenr = btrfs_file_extent_disk_bytenr(leaf, fi);
|
Btrfs: Mixed back reference (FORWARD ROLLING FORMAT CHANGE)
This commit introduces a new kind of back reference for btrfs metadata.
Once a filesystem has been mounted with this commit, IT WILL NO LONGER
BE MOUNTABLE BY OLDER KERNELS.
When a tree block in subvolume tree is cow'd, the reference counts of all
extents it points to are increased by one. At transaction commit time,
the old root of the subvolume is recorded in a "dead root" data structure,
and the btree it points to is later walked, dropping reference counts
and freeing any blocks where the reference count goes to 0.
The increments done during cow and decrements done after commit cancel out,
and the walk is a very expensive way to go about freeing the blocks that
are no longer referenced by the new btree root. This commit reduces the
transaction overhead by avoiding the need for dead root records.
When a non-shared tree block is cow'd, we free the old block at once, and the
new block inherits old block's references. When a tree block with reference
count > 1 is cow'd, we increase the reference counts of all extents
the new block points to by one, and decrease the old block's reference count by
one.
This dead tree avoidance code removes the need to modify the reference
counts of lower level extents when a non-shared tree block is cow'd.
But we still need to update back ref for all pointers in the block.
This is because the location of the block is recorded in the back ref
item.
We can solve this by introducing a new type of back ref. The new
back ref provides information about pointer's key, level and in which
tree the pointer lives. This information allow us to find the pointer
by searching the tree. The shortcoming of the new back ref is that it
only works for pointers in tree blocks referenced by their owner trees.
This is mostly a problem for snapshots, where resolving one of these
fuzzy back references would be O(number_of_snapshots) and quite slow.
The solution used here is to use the fuzzy back references in the common
case where a given tree block is only referenced by one root,
and use the full back references when multiple roots have a reference
on a given block.
This commit adds per subvolume red-black tree to keep trace of cached
inodes. The red-black tree helps the balancing code to find cached
inodes whose inode numbers within a given range.
This commit improves the balancing code by introducing several data
structures to keep the state of balancing. The most important one
is the back ref cache. It caches how the upper level tree blocks are
referenced. This greatly reduce the overhead of checking back ref.
The improved balancing code scales significantly better with a large
number of snapshots.
This is a very large commit and was written in a number of
pieces. But, they depend heavily on the disk format change and were
squashed together to make sure git bisect didn't end up in a
bad state wrt space balancing or the format change.
Signed-off-by: Yan Zheng <zheng.yan@oracle.com>
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2009-06-10 22:45:14 +08:00
|
|
|
extent_offset = btrfs_file_extent_offset(leaf, fi);
|
2008-10-31 02:20:02 +08:00
|
|
|
extent_end = found_key.offset +
|
|
|
|
btrfs_file_extent_num_bytes(leaf, fi);
|
|
|
|
if (extent_end <= start) {
|
|
|
|
path->slots[0]++;
|
|
|
|
goto next_slot;
|
|
|
|
}
|
2008-12-12 23:03:38 +08:00
|
|
|
if (disk_bytenr == 0)
|
|
|
|
goto out_check;
|
2008-10-31 02:20:02 +08:00
|
|
|
if (btrfs_file_extent_compression(leaf, fi) ||
|
|
|
|
btrfs_file_extent_encryption(leaf, fi) ||
|
|
|
|
btrfs_file_extent_other_encoding(leaf, fi))
|
|
|
|
goto out_check;
|
2008-10-31 02:25:28 +08:00
|
|
|
if (extent_type == BTRFS_FILE_EXTENT_REG && !force)
|
|
|
|
goto out_check;
|
2008-12-12 05:30:39 +08:00
|
|
|
if (btrfs_extent_readonly(root, disk_bytenr))
|
2008-10-31 02:20:02 +08:00
|
|
|
goto out_check;
|
2008-12-12 23:03:38 +08:00
|
|
|
if (btrfs_cross_ref_exist(trans, root, inode->i_ino,
|
Btrfs: Mixed back reference (FORWARD ROLLING FORMAT CHANGE)
This commit introduces a new kind of back reference for btrfs metadata.
Once a filesystem has been mounted with this commit, IT WILL NO LONGER
BE MOUNTABLE BY OLDER KERNELS.
When a tree block in subvolume tree is cow'd, the reference counts of all
extents it points to are increased by one. At transaction commit time,
the old root of the subvolume is recorded in a "dead root" data structure,
and the btree it points to is later walked, dropping reference counts
and freeing any blocks where the reference count goes to 0.
The increments done during cow and decrements done after commit cancel out,
and the walk is a very expensive way to go about freeing the blocks that
are no longer referenced by the new btree root. This commit reduces the
transaction overhead by avoiding the need for dead root records.
When a non-shared tree block is cow'd, we free the old block at once, and the
new block inherits old block's references. When a tree block with reference
count > 1 is cow'd, we increase the reference counts of all extents
the new block points to by one, and decrease the old block's reference count by
one.
This dead tree avoidance code removes the need to modify the reference
counts of lower level extents when a non-shared tree block is cow'd.
But we still need to update back ref for all pointers in the block.
This is because the location of the block is recorded in the back ref
item.
We can solve this by introducing a new type of back ref. The new
back ref provides information about pointer's key, level and in which
tree the pointer lives. This information allow us to find the pointer
by searching the tree. The shortcoming of the new back ref is that it
only works for pointers in tree blocks referenced by their owner trees.
This is mostly a problem for snapshots, where resolving one of these
fuzzy back references would be O(number_of_snapshots) and quite slow.
The solution used here is to use the fuzzy back references in the common
case where a given tree block is only referenced by one root,
and use the full back references when multiple roots have a reference
on a given block.
This commit adds per subvolume red-black tree to keep trace of cached
inodes. The red-black tree helps the balancing code to find cached
inodes whose inode numbers within a given range.
This commit improves the balancing code by introducing several data
structures to keep the state of balancing. The most important one
is the back ref cache. It caches how the upper level tree blocks are
referenced. This greatly reduce the overhead of checking back ref.
The improved balancing code scales significantly better with a large
number of snapshots.
This is a very large commit and was written in a number of
pieces. But, they depend heavily on the disk format change and were
squashed together to make sure git bisect didn't end up in a
bad state wrt space balancing or the format change.
Signed-off-by: Yan Zheng <zheng.yan@oracle.com>
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2009-06-10 22:45:14 +08:00
|
|
|
found_key.offset -
|
|
|
|
extent_offset, disk_bytenr))
|
2008-12-12 23:03:38 +08:00
|
|
|
goto out_check;
|
Btrfs: Mixed back reference (FORWARD ROLLING FORMAT CHANGE)
This commit introduces a new kind of back reference for btrfs metadata.
Once a filesystem has been mounted with this commit, IT WILL NO LONGER
BE MOUNTABLE BY OLDER KERNELS.
When a tree block in subvolume tree is cow'd, the reference counts of all
extents it points to are increased by one. At transaction commit time,
the old root of the subvolume is recorded in a "dead root" data structure,
and the btree it points to is later walked, dropping reference counts
and freeing any blocks where the reference count goes to 0.
The increments done during cow and decrements done after commit cancel out,
and the walk is a very expensive way to go about freeing the blocks that
are no longer referenced by the new btree root. This commit reduces the
transaction overhead by avoiding the need for dead root records.
When a non-shared tree block is cow'd, we free the old block at once, and the
new block inherits old block's references. When a tree block with reference
count > 1 is cow'd, we increase the reference counts of all extents
the new block points to by one, and decrease the old block's reference count by
one.
This dead tree avoidance code removes the need to modify the reference
counts of lower level extents when a non-shared tree block is cow'd.
But we still need to update back ref for all pointers in the block.
This is because the location of the block is recorded in the back ref
item.
We can solve this by introducing a new type of back ref. The new
back ref provides information about pointer's key, level and in which
tree the pointer lives. This information allow us to find the pointer
by searching the tree. The shortcoming of the new back ref is that it
only works for pointers in tree blocks referenced by their owner trees.
This is mostly a problem for snapshots, where resolving one of these
fuzzy back references would be O(number_of_snapshots) and quite slow.
The solution used here is to use the fuzzy back references in the common
case where a given tree block is only referenced by one root,
and use the full back references when multiple roots have a reference
on a given block.
This commit adds per subvolume red-black tree to keep trace of cached
inodes. The red-black tree helps the balancing code to find cached
inodes whose inode numbers within a given range.
This commit improves the balancing code by introducing several data
structures to keep the state of balancing. The most important one
is the back ref cache. It caches how the upper level tree blocks are
referenced. This greatly reduce the overhead of checking back ref.
The improved balancing code scales significantly better with a large
number of snapshots.
This is a very large commit and was written in a number of
pieces. But, they depend heavily on the disk format change and were
squashed together to make sure git bisect didn't end up in a
bad state wrt space balancing or the format change.
Signed-off-by: Yan Zheng <zheng.yan@oracle.com>
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2009-06-10 22:45:14 +08:00
|
|
|
disk_bytenr += extent_offset;
|
2008-12-12 23:03:38 +08:00
|
|
|
disk_bytenr += cur_offset - found_key.offset;
|
|
|
|
num_bytes = min(end + 1, extent_end) - cur_offset;
|
|
|
|
/*
|
|
|
|
* force cow if csum exists in the range.
|
|
|
|
* this ensure that csum for a given extent are
|
|
|
|
* either valid or do not exist.
|
|
|
|
*/
|
|
|
|
if (csum_exist_in_range(root, disk_bytenr, num_bytes))
|
|
|
|
goto out_check;
|
2008-10-31 02:20:02 +08:00
|
|
|
nocow = 1;
|
|
|
|
} else if (extent_type == BTRFS_FILE_EXTENT_INLINE) {
|
|
|
|
extent_end = found_key.offset +
|
|
|
|
btrfs_file_extent_inline_len(leaf, fi);
|
|
|
|
extent_end = ALIGN(extent_end, root->sectorsize);
|
|
|
|
} else {
|
|
|
|
BUG_ON(1);
|
|
|
|
}
|
|
|
|
out_check:
|
|
|
|
if (extent_end <= start) {
|
|
|
|
path->slots[0]++;
|
|
|
|
goto next_slot;
|
|
|
|
}
|
|
|
|
if (!nocow) {
|
|
|
|
if (cow_start == (u64)-1)
|
|
|
|
cow_start = cur_offset;
|
|
|
|
cur_offset = extent_end;
|
|
|
|
if (cur_offset > end)
|
|
|
|
break;
|
|
|
|
path->slots[0]++;
|
|
|
|
goto next_slot;
|
2008-08-06 01:05:02 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
btrfs_release_path(root, path);
|
2008-10-31 02:20:02 +08:00
|
|
|
if (cow_start != (u64)-1) {
|
|
|
|
ret = cow_file_range(inode, locked_page, cow_start,
|
2008-11-07 11:02:51 +08:00
|
|
|
found_key.offset - 1, page_started,
|
|
|
|
nr_written, 1);
|
2008-10-31 02:20:02 +08:00
|
|
|
BUG_ON(ret);
|
|
|
|
cow_start = (u64)-1;
|
2008-08-06 01:05:02 +08:00
|
|
|
}
|
2008-10-31 02:20:02 +08:00
|
|
|
|
2008-10-31 02:25:28 +08:00
|
|
|
if (extent_type == BTRFS_FILE_EXTENT_PREALLOC) {
|
|
|
|
struct extent_map *em;
|
|
|
|
struct extent_map_tree *em_tree;
|
|
|
|
em_tree = &BTRFS_I(inode)->extent_tree;
|
|
|
|
em = alloc_extent_map(GFP_NOFS);
|
|
|
|
em->start = cur_offset;
|
2008-11-11 00:53:33 +08:00
|
|
|
em->orig_start = em->start;
|
2008-10-31 02:25:28 +08:00
|
|
|
em->len = num_bytes;
|
|
|
|
em->block_len = num_bytes;
|
|
|
|
em->block_start = disk_bytenr;
|
|
|
|
em->bdev = root->fs_info->fs_devices->latest_bdev;
|
|
|
|
set_bit(EXTENT_FLAG_PINNED, &em->flags);
|
|
|
|
while (1) {
|
2009-09-03 04:24:52 +08:00
|
|
|
write_lock(&em_tree->lock);
|
2008-10-31 02:25:28 +08:00
|
|
|
ret = add_extent_mapping(em_tree, em);
|
2009-09-03 04:24:52 +08:00
|
|
|
write_unlock(&em_tree->lock);
|
2008-10-31 02:25:28 +08:00
|
|
|
if (ret != -EEXIST) {
|
|
|
|
free_extent_map(em);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
btrfs_drop_extent_cache(inode, em->start,
|
|
|
|
em->start + em->len - 1, 0);
|
|
|
|
}
|
|
|
|
type = BTRFS_ORDERED_PREALLOC;
|
|
|
|
} else {
|
|
|
|
type = BTRFS_ORDERED_NOCOW;
|
|
|
|
}
|
2008-10-31 02:20:02 +08:00
|
|
|
|
|
|
|
ret = btrfs_add_ordered_extent(inode, cur_offset, disk_bytenr,
|
2008-10-31 02:25:28 +08:00
|
|
|
num_bytes, num_bytes, type);
|
|
|
|
BUG_ON(ret);
|
2008-11-07 11:02:51 +08:00
|
|
|
|
2008-10-31 02:25:28 +08:00
|
|
|
extent_clear_unlock_delalloc(inode, &BTRFS_I(inode)->io_tree,
|
|
|
|
cur_offset, cur_offset + num_bytes - 1,
|
2009-09-03 04:53:46 +08:00
|
|
|
locked_page, 1, 1, 1, 0, 0, 0, 1);
|
2008-10-31 02:20:02 +08:00
|
|
|
cur_offset = extent_end;
|
|
|
|
if (cur_offset > end)
|
|
|
|
break;
|
2007-12-18 09:14:01 +08:00
|
|
|
}
|
2008-10-31 02:20:02 +08:00
|
|
|
btrfs_release_path(root, path);
|
|
|
|
|
|
|
|
if (cur_offset <= end && cow_start == (u64)-1)
|
|
|
|
cow_start = cur_offset;
|
|
|
|
if (cow_start != (u64)-1) {
|
|
|
|
ret = cow_file_range(inode, locked_page, cow_start, end,
|
2008-11-07 11:02:51 +08:00
|
|
|
page_started, nr_written, 1);
|
2008-10-31 02:20:02 +08:00
|
|
|
BUG_ON(ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = btrfs_end_transaction(trans, root);
|
|
|
|
BUG_ON(ret);
|
2008-08-06 01:05:02 +08:00
|
|
|
btrfs_free_path(path);
|
2008-10-31 02:20:02 +08:00
|
|
|
return 0;
|
2007-12-18 09:14:01 +08:00
|
|
|
}
|
|
|
|
|
2008-09-30 03:18:18 +08:00
|
|
|
/*
|
|
|
|
* extent_io.c call back to do delayed allocation processing
|
|
|
|
*/
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
static int run_delalloc_range(struct inode *inode, struct page *locked_page,
|
2008-11-07 11:02:51 +08:00
|
|
|
u64 start, u64 end, int *page_started,
|
|
|
|
unsigned long *nr_written)
|
2007-12-18 09:14:01 +08:00
|
|
|
{
|
|
|
|
int ret;
|
2009-03-13 08:12:45 +08:00
|
|
|
struct btrfs_root *root = BTRFS_I(inode)->root;
|
2008-06-26 04:01:30 +08:00
|
|
|
|
2009-04-17 16:37:41 +08:00
|
|
|
if (BTRFS_I(inode)->flags & BTRFS_INODE_NODATACOW)
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
ret = run_delalloc_nocow(inode, locked_page, start, end,
|
2009-01-06 10:25:51 +08:00
|
|
|
page_started, 1, nr_written);
|
2009-04-17 16:37:41 +08:00
|
|
|
else if (BTRFS_I(inode)->flags & BTRFS_INODE_PREALLOC)
|
2008-10-31 02:25:28 +08:00
|
|
|
ret = run_delalloc_nocow(inode, locked_page, start, end,
|
2009-01-06 10:25:51 +08:00
|
|
|
page_started, 0, nr_written);
|
2009-03-13 08:12:45 +08:00
|
|
|
else if (!btrfs_test_opt(root, COMPRESS))
|
|
|
|
ret = cow_file_range(inode, locked_page, start, end,
|
|
|
|
page_started, nr_written, 1);
|
2007-12-18 09:14:01 +08:00
|
|
|
else
|
2008-11-07 11:02:51 +08:00
|
|
|
ret = cow_file_range_async(inode, locked_page, start, end,
|
2009-01-06 10:25:51 +08:00
|
|
|
page_started, nr_written);
|
2007-08-28 04:49:44 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2009-09-12 04:12:44 +08:00
|
|
|
static int btrfs_split_extent_hook(struct inode *inode,
|
|
|
|
struct extent_state *orig, u64 split)
|
|
|
|
{
|
|
|
|
struct btrfs_root *root = BTRFS_I(inode)->root;
|
|
|
|
u64 size;
|
|
|
|
|
|
|
|
if (!(orig->state & EXTENT_DELALLOC))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
size = orig->end - orig->start + 1;
|
|
|
|
if (size > root->fs_info->max_extent) {
|
|
|
|
u64 num_extents;
|
|
|
|
u64 new_size;
|
|
|
|
|
|
|
|
new_size = orig->end - split + 1;
|
|
|
|
num_extents = div64_u64(size + root->fs_info->max_extent - 1,
|
|
|
|
root->fs_info->max_extent);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* if we break a large extent up then leave delalloc_extents be,
|
|
|
|
* since we've already accounted for the large extent.
|
|
|
|
*/
|
|
|
|
if (div64_u64(new_size + root->fs_info->max_extent - 1,
|
|
|
|
root->fs_info->max_extent) < num_extents)
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
BTRFS_I(inode)->delalloc_extents++;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* extent_io.c merge_extent_hook, used to track merged delayed allocation
|
|
|
|
* extents so we can keep track of new extents that are just merged onto old
|
|
|
|
* extents, such as when we are doing sequential writes, so we can properly
|
|
|
|
* account for the metadata space we'll need.
|
|
|
|
*/
|
|
|
|
static int btrfs_merge_extent_hook(struct inode *inode,
|
|
|
|
struct extent_state *new,
|
|
|
|
struct extent_state *other)
|
|
|
|
{
|
|
|
|
struct btrfs_root *root = BTRFS_I(inode)->root;
|
|
|
|
u64 new_size, old_size;
|
|
|
|
u64 num_extents;
|
|
|
|
|
|
|
|
/* not delalloc, ignore it */
|
|
|
|
if (!(other->state & EXTENT_DELALLOC))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
old_size = other->end - other->start + 1;
|
|
|
|
if (new->start < other->start)
|
|
|
|
new_size = other->end - new->start + 1;
|
|
|
|
else
|
|
|
|
new_size = new->end - other->start + 1;
|
|
|
|
|
|
|
|
/* we're not bigger than the max, unreserve the space and go */
|
|
|
|
if (new_size <= root->fs_info->max_extent) {
|
|
|
|
BTRFS_I(inode)->delalloc_extents--;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we grew by another max_extent, just return, we want to keep that
|
|
|
|
* reserved amount.
|
|
|
|
*/
|
|
|
|
num_extents = div64_u64(old_size + root->fs_info->max_extent - 1,
|
|
|
|
root->fs_info->max_extent);
|
|
|
|
if (div64_u64(new_size + root->fs_info->max_extent - 1,
|
|
|
|
root->fs_info->max_extent) > num_extents)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
BTRFS_I(inode)->delalloc_extents--;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-09-30 03:18:18 +08:00
|
|
|
/*
|
|
|
|
* extent_io.c set_bit_hook, used to track delayed allocation
|
|
|
|
* bytes in this file, and to maintain the list of inodes that
|
|
|
|
* have pending delalloc work to be done.
|
|
|
|
*/
|
2008-12-02 22:54:17 +08:00
|
|
|
static int btrfs_set_bit_hook(struct inode *inode, u64 start, u64 end,
|
2008-02-01 00:05:37 +08:00
|
|
|
unsigned long old, unsigned long bits)
|
2008-01-30 04:55:23 +08:00
|
|
|
{
|
2009-09-12 04:12:44 +08:00
|
|
|
|
2008-12-16 04:54:40 +08:00
|
|
|
/*
|
|
|
|
* set_bit and clear bit hooks normally require _irqsave/restore
|
|
|
|
* but in this case, we are only testeing for the DELALLOC
|
|
|
|
* bit, which is only set or cleared with irqs on
|
|
|
|
*/
|
2008-02-01 00:05:37 +08:00
|
|
|
if (!(old & EXTENT_DELALLOC) && (bits & EXTENT_DELALLOC)) {
|
2008-01-30 04:55:23 +08:00
|
|
|
struct btrfs_root *root = BTRFS_I(inode)->root;
|
2009-09-12 04:12:44 +08:00
|
|
|
|
|
|
|
BTRFS_I(inode)->delalloc_extents++;
|
2009-02-21 00:00:09 +08:00
|
|
|
btrfs_delalloc_reserve_space(root, inode, end - start + 1);
|
2008-12-16 04:54:40 +08:00
|
|
|
spin_lock(&root->fs_info->delalloc_lock);
|
2008-02-09 02:49:28 +08:00
|
|
|
BTRFS_I(inode)->delalloc_bytes += end - start + 1;
|
2008-01-30 04:55:23 +08:00
|
|
|
root->fs_info->delalloc_bytes += end - start + 1;
|
2008-08-05 11:17:27 +08:00
|
|
|
if (list_empty(&BTRFS_I(inode)->delalloc_inodes)) {
|
|
|
|
list_add_tail(&BTRFS_I(inode)->delalloc_inodes,
|
|
|
|
&root->fs_info->delalloc_inodes);
|
|
|
|
}
|
2008-12-16 04:54:40 +08:00
|
|
|
spin_unlock(&root->fs_info->delalloc_lock);
|
2008-01-30 04:55:23 +08:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-09-30 03:18:18 +08:00
|
|
|
/*
|
|
|
|
* extent_io.c clear_bit_hook, see set_bit_hook for why
|
|
|
|
*/
|
2009-09-12 04:12:44 +08:00
|
|
|
static int btrfs_clear_bit_hook(struct inode *inode,
|
|
|
|
struct extent_state *state, unsigned long bits)
|
2008-01-30 04:55:23 +08:00
|
|
|
{
|
2008-12-16 04:54:40 +08:00
|
|
|
/*
|
|
|
|
* set_bit and clear bit hooks normally require _irqsave/restore
|
|
|
|
* but in this case, we are only testeing for the DELALLOC
|
|
|
|
* bit, which is only set or cleared with irqs on
|
|
|
|
*/
|
2009-09-12 04:12:44 +08:00
|
|
|
if ((state->state & EXTENT_DELALLOC) && (bits & EXTENT_DELALLOC)) {
|
2008-01-30 04:55:23 +08:00
|
|
|
struct btrfs_root *root = BTRFS_I(inode)->root;
|
2008-04-23 01:26:47 +08:00
|
|
|
|
2009-09-12 04:12:44 +08:00
|
|
|
BTRFS_I(inode)->delalloc_extents--;
|
|
|
|
btrfs_unreserve_metadata_for_delalloc(root, inode, 1);
|
|
|
|
|
2008-12-16 04:54:40 +08:00
|
|
|
spin_lock(&root->fs_info->delalloc_lock);
|
2009-09-12 04:12:44 +08:00
|
|
|
if (state->end - state->start + 1 >
|
|
|
|
root->fs_info->delalloc_bytes) {
|
2009-01-06 10:25:51 +08:00
|
|
|
printk(KERN_INFO "btrfs warning: delalloc account "
|
|
|
|
"%llu %llu\n",
|
2009-09-12 04:12:44 +08:00
|
|
|
(unsigned long long)
|
|
|
|
state->end - state->start + 1,
|
2009-01-06 10:25:51 +08:00
|
|
|
(unsigned long long)
|
|
|
|
root->fs_info->delalloc_bytes);
|
2009-02-21 00:00:09 +08:00
|
|
|
btrfs_delalloc_free_space(root, inode, (u64)-1);
|
2008-02-01 00:05:37 +08:00
|
|
|
root->fs_info->delalloc_bytes = 0;
|
2008-02-09 02:49:28 +08:00
|
|
|
BTRFS_I(inode)->delalloc_bytes = 0;
|
2008-02-01 00:05:37 +08:00
|
|
|
} else {
|
2009-02-21 00:00:09 +08:00
|
|
|
btrfs_delalloc_free_space(root, inode,
|
2009-09-12 04:12:44 +08:00
|
|
|
state->end -
|
|
|
|
state->start + 1);
|
|
|
|
root->fs_info->delalloc_bytes -= state->end -
|
|
|
|
state->start + 1;
|
|
|
|
BTRFS_I(inode)->delalloc_bytes -= state->end -
|
|
|
|
state->start + 1;
|
2008-02-01 00:05:37 +08:00
|
|
|
}
|
2008-08-05 11:17:27 +08:00
|
|
|
if (BTRFS_I(inode)->delalloc_bytes == 0 &&
|
|
|
|
!list_empty(&BTRFS_I(inode)->delalloc_inodes)) {
|
|
|
|
list_del_init(&BTRFS_I(inode)->delalloc_inodes);
|
|
|
|
}
|
2008-12-16 04:54:40 +08:00
|
|
|
spin_unlock(&root->fs_info->delalloc_lock);
|
2008-01-30 04:55:23 +08:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-09-30 03:18:18 +08:00
|
|
|
/*
|
|
|
|
* extent_io.c merge_bio_hook, this must check the chunk tree to make sure
|
|
|
|
* we don't create bios that span stripes or chunks
|
|
|
|
*/
|
2008-03-25 03:02:07 +08:00
|
|
|
int btrfs_merge_bio_hook(struct page *page, unsigned long offset,
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
size_t size, struct bio *bio,
|
|
|
|
unsigned long bio_flags)
|
2008-03-25 03:02:07 +08:00
|
|
|
{
|
|
|
|
struct btrfs_root *root = BTRFS_I(page->mapping->host)->root;
|
|
|
|
struct btrfs_mapping_tree *map_tree;
|
2008-10-04 04:31:08 +08:00
|
|
|
u64 logical = (u64)bio->bi_sector << 9;
|
2008-03-25 03:02:07 +08:00
|
|
|
u64 length = 0;
|
|
|
|
u64 map_length;
|
|
|
|
int ret;
|
|
|
|
|
2008-11-07 11:02:51 +08:00
|
|
|
if (bio_flags & EXTENT_BIO_COMPRESSED)
|
|
|
|
return 0;
|
|
|
|
|
2008-04-21 22:03:05 +08:00
|
|
|
length = bio->bi_size;
|
2008-03-25 03:02:07 +08:00
|
|
|
map_tree = &root->fs_info->mapping_tree;
|
|
|
|
map_length = length;
|
2008-04-10 04:28:12 +08:00
|
|
|
ret = btrfs_map_block(map_tree, READ, logical,
|
2008-04-10 04:28:12 +08:00
|
|
|
&map_length, NULL, 0);
|
2008-04-10 04:28:12 +08:00
|
|
|
|
2009-01-06 10:25:51 +08:00
|
|
|
if (map_length < length + size)
|
2008-03-25 03:02:07 +08:00
|
|
|
return 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-09-30 03:18:18 +08:00
|
|
|
/*
|
|
|
|
* in order to insert checksums into the metadata in large chunks,
|
|
|
|
* we wait until bio submission time. All the pages in the bio are
|
|
|
|
* checksummed and sums are attached onto the ordered extent record.
|
|
|
|
*
|
|
|
|
* At IO completion time the cums attached on the ordered extent record
|
|
|
|
* are inserted into the btree
|
|
|
|
*/
|
2009-01-06 10:25:51 +08:00
|
|
|
static int __btrfs_submit_bio_start(struct inode *inode, int rw,
|
|
|
|
struct bio *bio, int mirror_num,
|
|
|
|
unsigned long bio_flags)
|
2008-02-21 01:07:25 +08:00
|
|
|
{
|
|
|
|
struct btrfs_root *root = BTRFS_I(inode)->root;
|
|
|
|
int ret = 0;
|
2008-04-16 23:15:20 +08:00
|
|
|
|
Btrfs: move data checksumming into a dedicated tree
Btrfs stores checksums for each data block. Until now, they have
been stored in the subvolume trees, indexed by the inode that is
referencing the data block. This means that when we read the inode,
we've probably read in at least some checksums as well.
But, this has a few problems:
* The checksums are indexed by logical offset in the file. When
compression is on, this means we have to do the expensive checksumming
on the uncompressed data. It would be faster if we could checksum
the compressed data instead.
* If we implement encryption, we'll be checksumming the plain text and
storing that on disk. This is significantly less secure.
* For either compression or encryption, we have to get the plain text
back before we can verify the checksum as correct. This makes the raid
layer balancing and extent moving much more expensive.
* It makes the front end caching code more complex, as we have touch
the subvolume and inodes as we cache extents.
* There is potentitally one copy of the checksum in each subvolume
referencing an extent.
The solution used here is to store the extent checksums in a dedicated
tree. This allows us to index the checksums by phyiscal extent
start and length. It means:
* The checksum is against the data stored on disk, after any compression
or encryption is done.
* The checksum is stored in a central location, and can be verified without
following back references, or reading inodes.
This makes compression significantly faster by reducing the amount of
data that needs to be checksummed. It will also allow much faster
raid management code in general.
The checksums are indexed by a key with a fixed objectid (a magic value
in ctree.h) and offset set to the starting byte of the extent. This
allows us to copy the checksum items into the fsync log tree directly (or
any other tree), without having to invent a second format for them.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-12-09 05:58:54 +08:00
|
|
|
ret = btrfs_csum_one_bio(root, inode, bio, 0, 0);
|
2008-04-16 23:14:51 +08:00
|
|
|
BUG_ON(ret);
|
Btrfs: Add ordered async work queues
Btrfs uses kernel threads to create async work queues for cpu intensive
operations such as checksumming and decompression. These work well,
but they make it difficult to keep IO order intact.
A single writepages call from pdflush or fsync will turn into a number
of bios, and each bio is checksummed in parallel. Once the checksum is
computed, the bio is sent down to the disk, and since we don't control
the order in which the parallel operations happen, they might go down to
the disk in almost any order.
The code deals with this somewhat by having deep work queues for a single
kernel thread, making it very likely that a single thread will process all
the bios for a single inode.
This patch introduces an explicitly ordered work queue. As work structs
are placed into the queue they are put onto the tail of a list. They have
three callbacks:
->func (cpu intensive processing here)
->ordered_func (order sensitive processing here)
->ordered_free (free the work struct, all processing is done)
The work struct has three callbacks. The func callback does the cpu intensive
work, and when it completes the work struct is marked as done.
Every time a work struct completes, the list is checked to see if the head
is marked as done. If so the ordered_func callback is used to do the
order sensitive processing and the ordered_free callback is used to do
any cleanup. Then we loop back and check the head of the list again.
This patch also changes the checksumming code to use the ordered workqueues.
One a 4 drive array, it increases streaming writes from 280MB/s to 350MB/s.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-11-07 11:03:00 +08:00
|
|
|
return 0;
|
|
|
|
}
|
2008-04-16 23:15:20 +08:00
|
|
|
|
Btrfs: Add ordered async work queues
Btrfs uses kernel threads to create async work queues for cpu intensive
operations such as checksumming and decompression. These work well,
but they make it difficult to keep IO order intact.
A single writepages call from pdflush or fsync will turn into a number
of bios, and each bio is checksummed in parallel. Once the checksum is
computed, the bio is sent down to the disk, and since we don't control
the order in which the parallel operations happen, they might go down to
the disk in almost any order.
The code deals with this somewhat by having deep work queues for a single
kernel thread, making it very likely that a single thread will process all
the bios for a single inode.
This patch introduces an explicitly ordered work queue. As work structs
are placed into the queue they are put onto the tail of a list. They have
three callbacks:
->func (cpu intensive processing here)
->ordered_func (order sensitive processing here)
->ordered_free (free the work struct, all processing is done)
The work struct has three callbacks. The func callback does the cpu intensive
work, and when it completes the work struct is marked as done.
Every time a work struct completes, the list is checked to see if the head
is marked as done. If so the ordered_func callback is used to do the
order sensitive processing and the ordered_free callback is used to do
any cleanup. Then we loop back and check the head of the list again.
This patch also changes the checksumming code to use the ordered workqueues.
One a 4 drive array, it increases streaming writes from 280MB/s to 350MB/s.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-11-07 11:03:00 +08:00
|
|
|
/*
|
|
|
|
* in order to insert checksums into the metadata in large chunks,
|
|
|
|
* we wait until bio submission time. All the pages in the bio are
|
|
|
|
* checksummed and sums are attached onto the ordered extent record.
|
|
|
|
*
|
|
|
|
* At IO completion time the cums attached on the ordered extent record
|
|
|
|
* are inserted into the btree
|
|
|
|
*/
|
2008-12-02 22:54:17 +08:00
|
|
|
static int __btrfs_submit_bio_done(struct inode *inode, int rw, struct bio *bio,
|
Btrfs: Add ordered async work queues
Btrfs uses kernel threads to create async work queues for cpu intensive
operations such as checksumming and decompression. These work well,
but they make it difficult to keep IO order intact.
A single writepages call from pdflush or fsync will turn into a number
of bios, and each bio is checksummed in parallel. Once the checksum is
computed, the bio is sent down to the disk, and since we don't control
the order in which the parallel operations happen, they might go down to
the disk in almost any order.
The code deals with this somewhat by having deep work queues for a single
kernel thread, making it very likely that a single thread will process all
the bios for a single inode.
This patch introduces an explicitly ordered work queue. As work structs
are placed into the queue they are put onto the tail of a list. They have
three callbacks:
->func (cpu intensive processing here)
->ordered_func (order sensitive processing here)
->ordered_free (free the work struct, all processing is done)
The work struct has three callbacks. The func callback does the cpu intensive
work, and when it completes the work struct is marked as done.
Every time a work struct completes, the list is checked to see if the head
is marked as done. If so the ordered_func callback is used to do the
order sensitive processing and the ordered_free callback is used to do
any cleanup. Then we loop back and check the head of the list again.
This patch also changes the checksumming code to use the ordered workqueues.
One a 4 drive array, it increases streaming writes from 280MB/s to 350MB/s.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-11-07 11:03:00 +08:00
|
|
|
int mirror_num, unsigned long bio_flags)
|
|
|
|
{
|
|
|
|
struct btrfs_root *root = BTRFS_I(inode)->root;
|
2008-06-12 04:50:36 +08:00
|
|
|
return btrfs_map_bio(root, rw, bio, mirror_num, 1);
|
2008-04-16 23:14:51 +08:00
|
|
|
}
|
|
|
|
|
2008-09-30 03:18:18 +08:00
|
|
|
/*
|
2008-12-18 03:51:42 +08:00
|
|
|
* extent_io.c submission hook. This does the right thing for csum calculation
|
|
|
|
* on write, or reading the csums from the tree before a read
|
2008-09-30 03:18:18 +08:00
|
|
|
*/
|
2008-12-02 22:54:17 +08:00
|
|
|
static int btrfs_submit_bio_hook(struct inode *inode, int rw, struct bio *bio,
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
int mirror_num, unsigned long bio_flags)
|
2008-04-16 23:14:51 +08:00
|
|
|
{
|
|
|
|
struct btrfs_root *root = BTRFS_I(inode)->root;
|
|
|
|
int ret = 0;
|
2008-10-31 02:23:13 +08:00
|
|
|
int skip_sum;
|
2008-04-16 23:14:51 +08:00
|
|
|
|
2009-04-17 16:37:41 +08:00
|
|
|
skip_sum = BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM;
|
2008-12-18 03:51:42 +08:00
|
|
|
|
2008-07-18 00:53:50 +08:00
|
|
|
ret = btrfs_bio_wq_end_io(root->fs_info, bio, 0);
|
|
|
|
BUG_ON(ret);
|
2008-02-21 01:07:25 +08:00
|
|
|
|
2008-08-20 21:44:52 +08:00
|
|
|
if (!(rw & (1 << BIO_RW))) {
|
Btrfs: move data checksumming into a dedicated tree
Btrfs stores checksums for each data block. Until now, they have
been stored in the subvolume trees, indexed by the inode that is
referencing the data block. This means that when we read the inode,
we've probably read in at least some checksums as well.
But, this has a few problems:
* The checksums are indexed by logical offset in the file. When
compression is on, this means we have to do the expensive checksumming
on the uncompressed data. It would be faster if we could checksum
the compressed data instead.
* If we implement encryption, we'll be checksumming the plain text and
storing that on disk. This is significantly less secure.
* For either compression or encryption, we have to get the plain text
back before we can verify the checksum as correct. This makes the raid
layer balancing and extent moving much more expensive.
* It makes the front end caching code more complex, as we have touch
the subvolume and inodes as we cache extents.
* There is potentitally one copy of the checksum in each subvolume
referencing an extent.
The solution used here is to store the extent checksums in a dedicated
tree. This allows us to index the checksums by phyiscal extent
start and length. It means:
* The checksum is against the data stored on disk, after any compression
or encryption is done.
* The checksum is stored in a central location, and can be verified without
following back references, or reading inodes.
This makes compression significantly faster by reducing the amount of
data that needs to be checksummed. It will also allow much faster
raid management code in general.
The checksums are indexed by a key with a fixed objectid (a magic value
in ctree.h) and offset set to the starting byte of the extent. This
allows us to copy the checksum items into the fsync log tree directly (or
any other tree), without having to invent a second format for them.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-12-09 05:58:54 +08:00
|
|
|
if (bio_flags & EXTENT_BIO_COMPRESSED) {
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
return btrfs_submit_compressed_read(inode, bio,
|
|
|
|
mirror_num, bio_flags);
|
Btrfs: move data checksumming into a dedicated tree
Btrfs stores checksums for each data block. Until now, they have
been stored in the subvolume trees, indexed by the inode that is
referencing the data block. This means that when we read the inode,
we've probably read in at least some checksums as well.
But, this has a few problems:
* The checksums are indexed by logical offset in the file. When
compression is on, this means we have to do the expensive checksumming
on the uncompressed data. It would be faster if we could checksum
the compressed data instead.
* If we implement encryption, we'll be checksumming the plain text and
storing that on disk. This is significantly less secure.
* For either compression or encryption, we have to get the plain text
back before we can verify the checksum as correct. This makes the raid
layer balancing and extent moving much more expensive.
* It makes the front end caching code more complex, as we have touch
the subvolume and inodes as we cache extents.
* There is potentitally one copy of the checksum in each subvolume
referencing an extent.
The solution used here is to store the extent checksums in a dedicated
tree. This allows us to index the checksums by phyiscal extent
start and length. It means:
* The checksum is against the data stored on disk, after any compression
or encryption is done.
* The checksum is stored in a central location, and can be verified without
following back references, or reading inodes.
This makes compression significantly faster by reducing the amount of
data that needs to be checksummed. It will also allow much faster
raid management code in general.
The checksums are indexed by a key with a fixed objectid (a magic value
in ctree.h) and offset set to the starting byte of the extent. This
allows us to copy the checksum items into the fsync log tree directly (or
any other tree), without having to invent a second format for them.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-12-09 05:58:54 +08:00
|
|
|
} else if (!skip_sum)
|
|
|
|
btrfs_lookup_bio_sums(root, inode, bio, NULL);
|
2008-08-20 21:44:52 +08:00
|
|
|
goto mapit;
|
2008-10-31 02:23:13 +08:00
|
|
|
} else if (!skip_sum) {
|
2008-12-12 23:03:38 +08:00
|
|
|
/* csum items have already been cloned */
|
|
|
|
if (root->root_key.objectid == BTRFS_DATA_RELOC_TREE_OBJECTID)
|
|
|
|
goto mapit;
|
2008-10-31 02:23:13 +08:00
|
|
|
/* we're doing a write, do the async checksumming */
|
|
|
|
return btrfs_wq_submit_bio(BTRFS_I(inode)->root->fs_info,
|
2008-04-16 23:14:51 +08:00
|
|
|
inode, rw, bio, mirror_num,
|
Btrfs: Add ordered async work queues
Btrfs uses kernel threads to create async work queues for cpu intensive
operations such as checksumming and decompression. These work well,
but they make it difficult to keep IO order intact.
A single writepages call from pdflush or fsync will turn into a number
of bios, and each bio is checksummed in parallel. Once the checksum is
computed, the bio is sent down to the disk, and since we don't control
the order in which the parallel operations happen, they might go down to
the disk in almost any order.
The code deals with this somewhat by having deep work queues for a single
kernel thread, making it very likely that a single thread will process all
the bios for a single inode.
This patch introduces an explicitly ordered work queue. As work structs
are placed into the queue they are put onto the tail of a list. They have
three callbacks:
->func (cpu intensive processing here)
->ordered_func (order sensitive processing here)
->ordered_free (free the work struct, all processing is done)
The work struct has three callbacks. The func callback does the cpu intensive
work, and when it completes the work struct is marked as done.
Every time a work struct completes, the list is checked to see if the head
is marked as done. If so the ordered_func callback is used to do the
order sensitive processing and the ordered_free callback is used to do
any cleanup. Then we loop back and check the head of the list again.
This patch also changes the checksumming code to use the ordered workqueues.
One a 4 drive array, it increases streaming writes from 280MB/s to 350MB/s.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-11-07 11:03:00 +08:00
|
|
|
bio_flags, __btrfs_submit_bio_start,
|
|
|
|
__btrfs_submit_bio_done);
|
2008-10-31 02:23:13 +08:00
|
|
|
}
|
|
|
|
|
2008-03-25 03:01:56 +08:00
|
|
|
mapit:
|
2008-06-12 04:50:36 +08:00
|
|
|
return btrfs_map_bio(root, rw, bio, mirror_num, 0);
|
2008-02-21 01:07:25 +08:00
|
|
|
}
|
2008-02-21 05:11:05 +08:00
|
|
|
|
2008-09-30 03:18:18 +08:00
|
|
|
/*
|
|
|
|
* given a list of ordered sums record them in the inode. This happens
|
|
|
|
* at IO completion time based on sums calculated at bio submission time.
|
|
|
|
*/
|
2008-07-18 00:54:15 +08:00
|
|
|
static noinline int add_pending_csums(struct btrfs_trans_handle *trans,
|
2008-07-18 00:53:50 +08:00
|
|
|
struct inode *inode, u64 file_offset,
|
|
|
|
struct list_head *list)
|
|
|
|
{
|
|
|
|
struct btrfs_ordered_sum *sum;
|
|
|
|
|
|
|
|
btrfs_set_trans_block_group(trans, inode);
|
2009-01-21 23:59:08 +08:00
|
|
|
|
|
|
|
list_for_each_entry(sum, list, list) {
|
Btrfs: move data checksumming into a dedicated tree
Btrfs stores checksums for each data block. Until now, they have
been stored in the subvolume trees, indexed by the inode that is
referencing the data block. This means that when we read the inode,
we've probably read in at least some checksums as well.
But, this has a few problems:
* The checksums are indexed by logical offset in the file. When
compression is on, this means we have to do the expensive checksumming
on the uncompressed data. It would be faster if we could checksum
the compressed data instead.
* If we implement encryption, we'll be checksumming the plain text and
storing that on disk. This is significantly less secure.
* For either compression or encryption, we have to get the plain text
back before we can verify the checksum as correct. This makes the raid
layer balancing and extent moving much more expensive.
* It makes the front end caching code more complex, as we have touch
the subvolume and inodes as we cache extents.
* There is potentitally one copy of the checksum in each subvolume
referencing an extent.
The solution used here is to store the extent checksums in a dedicated
tree. This allows us to index the checksums by phyiscal extent
start and length. It means:
* The checksum is against the data stored on disk, after any compression
or encryption is done.
* The checksum is stored in a central location, and can be verified without
following back references, or reading inodes.
This makes compression significantly faster by reducing the amount of
data that needs to be checksummed. It will also allow much faster
raid management code in general.
The checksums are indexed by a key with a fixed objectid (a magic value
in ctree.h) and offset set to the starting byte of the extent. This
allows us to copy the checksum items into the fsync log tree directly (or
any other tree), without having to invent a second format for them.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-12-09 05:58:54 +08:00
|
|
|
btrfs_csum_file_blocks(trans,
|
|
|
|
BTRFS_I(inode)->root->fs_info->csum_root, sum);
|
2008-07-18 00:53:50 +08:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-08-05 11:17:27 +08:00
|
|
|
int btrfs_set_extent_delalloc(struct inode *inode, u64 start, u64 end)
|
|
|
|
{
|
2009-01-06 10:25:51 +08:00
|
|
|
if ((end & (PAGE_CACHE_SIZE - 1)) == 0)
|
2008-11-07 11:02:51 +08:00
|
|
|
WARN_ON(1);
|
2008-08-05 11:17:27 +08:00
|
|
|
return set_extent_delalloc(&BTRFS_I(inode)->io_tree, start, end,
|
|
|
|
GFP_NOFS);
|
|
|
|
}
|
|
|
|
|
2008-09-30 03:18:18 +08:00
|
|
|
/* see btrfs_writepage_start_hook for details on why this is required */
|
2008-07-18 00:53:51 +08:00
|
|
|
struct btrfs_writepage_fixup {
|
|
|
|
struct page *page;
|
|
|
|
struct btrfs_work work;
|
|
|
|
};
|
|
|
|
|
2008-12-02 22:54:17 +08:00
|
|
|
static void btrfs_writepage_fixup_worker(struct btrfs_work *work)
|
2008-07-18 00:53:51 +08:00
|
|
|
{
|
|
|
|
struct btrfs_writepage_fixup *fixup;
|
|
|
|
struct btrfs_ordered_extent *ordered;
|
|
|
|
struct page *page;
|
|
|
|
struct inode *inode;
|
|
|
|
u64 page_start;
|
|
|
|
u64 page_end;
|
|
|
|
|
|
|
|
fixup = container_of(work, struct btrfs_writepage_fixup, work);
|
|
|
|
page = fixup->page;
|
2008-07-21 22:29:44 +08:00
|
|
|
again:
|
2008-07-18 00:53:51 +08:00
|
|
|
lock_page(page);
|
|
|
|
if (!page->mapping || !PageDirty(page) || !PageChecked(page)) {
|
|
|
|
ClearPageChecked(page);
|
|
|
|
goto out_page;
|
|
|
|
}
|
|
|
|
|
|
|
|
inode = page->mapping->host;
|
|
|
|
page_start = page_offset(page);
|
|
|
|
page_end = page_offset(page) + PAGE_CACHE_SIZE - 1;
|
|
|
|
|
|
|
|
lock_extent(&BTRFS_I(inode)->io_tree, page_start, page_end, GFP_NOFS);
|
2008-07-21 22:29:44 +08:00
|
|
|
|
|
|
|
/* already ordered? We're done */
|
2009-09-03 04:53:46 +08:00
|
|
|
if (PagePrivate2(page))
|
2008-07-18 00:53:51 +08:00
|
|
|
goto out;
|
2008-07-21 22:29:44 +08:00
|
|
|
|
|
|
|
ordered = btrfs_lookup_ordered_extent(inode, page_start);
|
|
|
|
if (ordered) {
|
|
|
|
unlock_extent(&BTRFS_I(inode)->io_tree, page_start,
|
|
|
|
page_end, GFP_NOFS);
|
|
|
|
unlock_page(page);
|
|
|
|
btrfs_start_ordered_extent(inode, ordered, 1);
|
|
|
|
goto again;
|
|
|
|
}
|
2008-07-18 00:53:51 +08:00
|
|
|
|
2008-08-05 11:17:27 +08:00
|
|
|
btrfs_set_extent_delalloc(inode, page_start, page_end);
|
2008-07-18 00:53:51 +08:00
|
|
|
ClearPageChecked(page);
|
|
|
|
out:
|
|
|
|
unlock_extent(&BTRFS_I(inode)->io_tree, page_start, page_end, GFP_NOFS);
|
|
|
|
out_page:
|
|
|
|
unlock_page(page);
|
|
|
|
page_cache_release(page);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* There are a few paths in the higher layers of the kernel that directly
|
|
|
|
* set the page dirty bit without asking the filesystem if it is a
|
|
|
|
* good idea. This causes problems because we want to make sure COW
|
|
|
|
* properly happens and the data=ordered rules are followed.
|
|
|
|
*
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
* In our case any range that doesn't have the ORDERED bit set
|
2008-07-18 00:53:51 +08:00
|
|
|
* hasn't been properly setup for IO. We kick off an async process
|
|
|
|
* to fix it up. The async helper will wait for ordered extents, set
|
|
|
|
* the delalloc bit and make it safe to write the page.
|
|
|
|
*/
|
2008-12-02 22:54:17 +08:00
|
|
|
static int btrfs_writepage_start_hook(struct page *page, u64 start, u64 end)
|
2008-07-18 00:53:51 +08:00
|
|
|
{
|
|
|
|
struct inode *inode = page->mapping->host;
|
|
|
|
struct btrfs_writepage_fixup *fixup;
|
|
|
|
struct btrfs_root *root = BTRFS_I(inode)->root;
|
|
|
|
|
2009-09-03 04:53:46 +08:00
|
|
|
/* this page is properly in the ordered list */
|
|
|
|
if (TestClearPagePrivate2(page))
|
2008-07-18 00:53:51 +08:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (PageChecked(page))
|
|
|
|
return -EAGAIN;
|
|
|
|
|
|
|
|
fixup = kzalloc(sizeof(*fixup), GFP_NOFS);
|
|
|
|
if (!fixup)
|
|
|
|
return -EAGAIN;
|
2008-07-22 23:18:09 +08:00
|
|
|
|
2008-07-18 00:53:51 +08:00
|
|
|
SetPageChecked(page);
|
|
|
|
page_cache_get(page);
|
|
|
|
fixup->work.func = btrfs_writepage_fixup_worker;
|
|
|
|
fixup->page = page;
|
|
|
|
btrfs_queue_worker(&root->fs_info->fixup_workers, &fixup->work);
|
|
|
|
return -EAGAIN;
|
|
|
|
}
|
|
|
|
|
2008-10-31 02:25:28 +08:00
|
|
|
static int insert_reserved_file_extent(struct btrfs_trans_handle *trans,
|
|
|
|
struct inode *inode, u64 file_pos,
|
|
|
|
u64 disk_bytenr, u64 disk_num_bytes,
|
|
|
|
u64 num_bytes, u64 ram_bytes,
|
2009-04-25 02:39:24 +08:00
|
|
|
u64 locked_end,
|
2008-10-31 02:25:28 +08:00
|
|
|
u8 compression, u8 encryption,
|
|
|
|
u16 other_encoding, int extent_type)
|
|
|
|
{
|
|
|
|
struct btrfs_root *root = BTRFS_I(inode)->root;
|
|
|
|
struct btrfs_file_extent_item *fi;
|
|
|
|
struct btrfs_path *path;
|
|
|
|
struct extent_buffer *leaf;
|
|
|
|
struct btrfs_key ins;
|
|
|
|
u64 hint;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
path = btrfs_alloc_path();
|
|
|
|
BUG_ON(!path);
|
|
|
|
|
2009-03-13 23:00:37 +08:00
|
|
|
path->leave_spinning = 1;
|
2009-09-12 00:27:37 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* we may be replacing one extent in the tree with another.
|
|
|
|
* The new extent is pinned in the extent map, and we don't want
|
|
|
|
* to drop it from the cache until it is completely in the btree.
|
|
|
|
*
|
|
|
|
* So, tell btrfs_drop_extents to leave this extent in the cache.
|
|
|
|
* the caller is expected to unpin it and allow it to be merged
|
|
|
|
* with the others.
|
|
|
|
*/
|
2008-10-31 02:25:28 +08:00
|
|
|
ret = btrfs_drop_extents(trans, root, inode, file_pos,
|
2009-04-25 02:39:24 +08:00
|
|
|
file_pos + num_bytes, locked_end,
|
2009-09-12 00:27:37 +08:00
|
|
|
file_pos, &hint, 0);
|
2008-10-31 02:25:28 +08:00
|
|
|
BUG_ON(ret);
|
|
|
|
|
|
|
|
ins.objectid = inode->i_ino;
|
|
|
|
ins.offset = file_pos;
|
|
|
|
ins.type = BTRFS_EXTENT_DATA_KEY;
|
|
|
|
ret = btrfs_insert_empty_item(trans, root, path, &ins, sizeof(*fi));
|
|
|
|
BUG_ON(ret);
|
|
|
|
leaf = path->nodes[0];
|
|
|
|
fi = btrfs_item_ptr(leaf, path->slots[0],
|
|
|
|
struct btrfs_file_extent_item);
|
|
|
|
btrfs_set_file_extent_generation(leaf, fi, trans->transid);
|
|
|
|
btrfs_set_file_extent_type(leaf, fi, extent_type);
|
|
|
|
btrfs_set_file_extent_disk_bytenr(leaf, fi, disk_bytenr);
|
|
|
|
btrfs_set_file_extent_disk_num_bytes(leaf, fi, disk_num_bytes);
|
|
|
|
btrfs_set_file_extent_offset(leaf, fi, 0);
|
|
|
|
btrfs_set_file_extent_num_bytes(leaf, fi, num_bytes);
|
|
|
|
btrfs_set_file_extent_ram_bytes(leaf, fi, ram_bytes);
|
|
|
|
btrfs_set_file_extent_compression(leaf, fi, compression);
|
|
|
|
btrfs_set_file_extent_encryption(leaf, fi, encryption);
|
|
|
|
btrfs_set_file_extent_other_encoding(leaf, fi, other_encoding);
|
2009-03-13 23:00:37 +08:00
|
|
|
|
|
|
|
btrfs_unlock_up_safe(path, 1);
|
|
|
|
btrfs_set_lock_blocking(leaf);
|
|
|
|
|
2008-10-31 02:25:28 +08:00
|
|
|
btrfs_mark_buffer_dirty(leaf);
|
|
|
|
|
|
|
|
inode_add_bytes(inode, num_bytes);
|
|
|
|
|
|
|
|
ins.objectid = disk_bytenr;
|
|
|
|
ins.offset = disk_num_bytes;
|
|
|
|
ins.type = BTRFS_EXTENT_ITEM_KEY;
|
Btrfs: Mixed back reference (FORWARD ROLLING FORMAT CHANGE)
This commit introduces a new kind of back reference for btrfs metadata.
Once a filesystem has been mounted with this commit, IT WILL NO LONGER
BE MOUNTABLE BY OLDER KERNELS.
When a tree block in subvolume tree is cow'd, the reference counts of all
extents it points to are increased by one. At transaction commit time,
the old root of the subvolume is recorded in a "dead root" data structure,
and the btree it points to is later walked, dropping reference counts
and freeing any blocks where the reference count goes to 0.
The increments done during cow and decrements done after commit cancel out,
and the walk is a very expensive way to go about freeing the blocks that
are no longer referenced by the new btree root. This commit reduces the
transaction overhead by avoiding the need for dead root records.
When a non-shared tree block is cow'd, we free the old block at once, and the
new block inherits old block's references. When a tree block with reference
count > 1 is cow'd, we increase the reference counts of all extents
the new block points to by one, and decrease the old block's reference count by
one.
This dead tree avoidance code removes the need to modify the reference
counts of lower level extents when a non-shared tree block is cow'd.
But we still need to update back ref for all pointers in the block.
This is because the location of the block is recorded in the back ref
item.
We can solve this by introducing a new type of back ref. The new
back ref provides information about pointer's key, level and in which
tree the pointer lives. This information allow us to find the pointer
by searching the tree. The shortcoming of the new back ref is that it
only works for pointers in tree blocks referenced by their owner trees.
This is mostly a problem for snapshots, where resolving one of these
fuzzy back references would be O(number_of_snapshots) and quite slow.
The solution used here is to use the fuzzy back references in the common
case where a given tree block is only referenced by one root,
and use the full back references when multiple roots have a reference
on a given block.
This commit adds per subvolume red-black tree to keep trace of cached
inodes. The red-black tree helps the balancing code to find cached
inodes whose inode numbers within a given range.
This commit improves the balancing code by introducing several data
structures to keep the state of balancing. The most important one
is the back ref cache. It caches how the upper level tree blocks are
referenced. This greatly reduce the overhead of checking back ref.
The improved balancing code scales significantly better with a large
number of snapshots.
This is a very large commit and was written in a number of
pieces. But, they depend heavily on the disk format change and were
squashed together to make sure git bisect didn't end up in a
bad state wrt space balancing or the format change.
Signed-off-by: Yan Zheng <zheng.yan@oracle.com>
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2009-06-10 22:45:14 +08:00
|
|
|
ret = btrfs_alloc_reserved_file_extent(trans, root,
|
|
|
|
root->root_key.objectid,
|
|
|
|
inode->i_ino, file_pos, &ins);
|
2008-10-31 02:25:28 +08:00
|
|
|
BUG_ON(ret);
|
|
|
|
btrfs_free_path(path);
|
2009-03-13 23:00:37 +08:00
|
|
|
|
2008-10-31 02:25:28 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-03-13 23:41:46 +08:00
|
|
|
/*
|
|
|
|
* helper function for btrfs_finish_ordered_io, this
|
|
|
|
* just reads in some of the csum leaves to prime them into ram
|
|
|
|
* before we start the transaction. It limits the amount of btree
|
|
|
|
* reads required while inside the transaction.
|
|
|
|
*/
|
|
|
|
static noinline void reada_csum(struct btrfs_root *root,
|
|
|
|
struct btrfs_path *path,
|
|
|
|
struct btrfs_ordered_extent *ordered_extent)
|
|
|
|
{
|
|
|
|
struct btrfs_ordered_sum *sum;
|
|
|
|
u64 bytenr;
|
|
|
|
|
|
|
|
sum = list_entry(ordered_extent->list.next, struct btrfs_ordered_sum,
|
|
|
|
list);
|
|
|
|
bytenr = sum->sums[0].bytenr;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* we don't care about the results, the point of this search is
|
|
|
|
* just to get the btree leaves into ram
|
|
|
|
*/
|
|
|
|
btrfs_lookup_csum(NULL, root->fs_info->csum_root, path, bytenr, 0);
|
|
|
|
}
|
|
|
|
|
2008-09-30 03:18:18 +08:00
|
|
|
/* as ordered data IO finishes, this gets called so we can finish
|
|
|
|
* an ordered extent if the range of bytes in the file it covers are
|
|
|
|
* fully written.
|
|
|
|
*/
|
2008-07-18 23:56:15 +08:00
|
|
|
static int btrfs_finish_ordered_io(struct inode *inode, u64 start, u64 end)
|
2008-07-18 00:53:50 +08:00
|
|
|
{
|
|
|
|
struct btrfs_root *root = BTRFS_I(inode)->root;
|
|
|
|
struct btrfs_trans_handle *trans;
|
2009-03-13 23:41:46 +08:00
|
|
|
struct btrfs_ordered_extent *ordered_extent = NULL;
|
2008-07-18 00:53:50 +08:00
|
|
|
struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree;
|
2009-03-13 08:12:45 +08:00
|
|
|
struct btrfs_path *path;
|
2008-10-31 02:25:28 +08:00
|
|
|
int compressed = 0;
|
2008-07-18 00:53:50 +08:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = btrfs_dec_test_ordered_pending(inode, start, end - start + 1);
|
2008-07-18 00:54:15 +08:00
|
|
|
if (!ret)
|
2008-07-18 00:53:50 +08:00
|
|
|
return 0;
|
|
|
|
|
2009-03-13 08:12:45 +08:00
|
|
|
/*
|
|
|
|
* before we join the transaction, try to do some of our IO.
|
|
|
|
* This will limit the amount of IO that we have to do with
|
|
|
|
* the transaction running. We're unlikely to need to do any
|
|
|
|
* IO if the file extents are new, the disk_i_size checks
|
|
|
|
* covers the most common case.
|
|
|
|
*/
|
|
|
|
if (start < BTRFS_I(inode)->disk_i_size) {
|
|
|
|
path = btrfs_alloc_path();
|
|
|
|
if (path) {
|
|
|
|
ret = btrfs_lookup_file_extent(NULL, root, path,
|
|
|
|
inode->i_ino,
|
|
|
|
start, 0);
|
2009-03-13 23:41:46 +08:00
|
|
|
ordered_extent = btrfs_lookup_ordered_extent(inode,
|
|
|
|
start);
|
|
|
|
if (!list_empty(&ordered_extent->list)) {
|
|
|
|
btrfs_release_path(root, path);
|
|
|
|
reada_csum(root, path, ordered_extent);
|
|
|
|
}
|
2009-03-13 08:12:45 +08:00
|
|
|
btrfs_free_path(path);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-07-18 00:54:14 +08:00
|
|
|
trans = btrfs_join_transaction(root, 1);
|
2008-07-18 00:53:50 +08:00
|
|
|
|
2009-03-13 23:41:46 +08:00
|
|
|
if (!ordered_extent)
|
|
|
|
ordered_extent = btrfs_lookup_ordered_extent(inode, start);
|
2008-07-18 00:53:50 +08:00
|
|
|
BUG_ON(!ordered_extent);
|
2008-08-06 01:05:02 +08:00
|
|
|
if (test_bit(BTRFS_ORDERED_NOCOW, &ordered_extent->flags))
|
|
|
|
goto nocow;
|
2008-07-18 00:53:50 +08:00
|
|
|
|
|
|
|
lock_extent(io_tree, ordered_extent->file_offset,
|
|
|
|
ordered_extent->file_offset + ordered_extent->len - 1,
|
|
|
|
GFP_NOFS);
|
|
|
|
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
if (test_bit(BTRFS_ORDERED_COMPRESSED, &ordered_extent->flags))
|
2008-10-31 02:25:28 +08:00
|
|
|
compressed = 1;
|
|
|
|
if (test_bit(BTRFS_ORDERED_PREALLOC, &ordered_extent->flags)) {
|
|
|
|
BUG_ON(compressed);
|
|
|
|
ret = btrfs_mark_extent_written(trans, root, inode,
|
|
|
|
ordered_extent->file_offset,
|
|
|
|
ordered_extent->file_offset +
|
|
|
|
ordered_extent->len);
|
|
|
|
BUG_ON(ret);
|
|
|
|
} else {
|
|
|
|
ret = insert_reserved_file_extent(trans, inode,
|
|
|
|
ordered_extent->file_offset,
|
|
|
|
ordered_extent->start,
|
|
|
|
ordered_extent->disk_len,
|
|
|
|
ordered_extent->len,
|
|
|
|
ordered_extent->len,
|
2009-04-25 02:39:24 +08:00
|
|
|
ordered_extent->file_offset +
|
|
|
|
ordered_extent->len,
|
2008-10-31 02:25:28 +08:00
|
|
|
compressed, 0, 0,
|
|
|
|
BTRFS_FILE_EXTENT_REG);
|
2009-09-12 00:27:37 +08:00
|
|
|
unpin_extent_cache(&BTRFS_I(inode)->extent_tree,
|
|
|
|
ordered_extent->file_offset,
|
|
|
|
ordered_extent->len);
|
2008-10-31 02:25:28 +08:00
|
|
|
BUG_ON(ret);
|
|
|
|
}
|
2008-07-18 00:53:50 +08:00
|
|
|
unlock_extent(io_tree, ordered_extent->file_offset,
|
|
|
|
ordered_extent->file_offset + ordered_extent->len - 1,
|
|
|
|
GFP_NOFS);
|
2008-08-06 01:05:02 +08:00
|
|
|
nocow:
|
2008-07-18 00:53:50 +08:00
|
|
|
add_pending_csums(trans, inode, ordered_extent->file_offset,
|
|
|
|
&ordered_extent->list);
|
|
|
|
|
2008-09-24 08:19:49 +08:00
|
|
|
mutex_lock(&BTRFS_I(inode)->extent_mutex);
|
2008-07-18 00:54:05 +08:00
|
|
|
btrfs_ordered_update_i_size(inode, ordered_extent);
|
2008-09-06 04:13:11 +08:00
|
|
|
btrfs_update_inode(trans, root, inode);
|
2008-07-18 00:53:50 +08:00
|
|
|
btrfs_remove_ordered_extent(inode, ordered_extent);
|
2008-09-24 08:19:49 +08:00
|
|
|
mutex_unlock(&BTRFS_I(inode)->extent_mutex);
|
2008-07-19 00:01:11 +08:00
|
|
|
|
2008-07-18 00:53:50 +08:00
|
|
|
/* once for us */
|
|
|
|
btrfs_put_ordered_extent(ordered_extent);
|
|
|
|
/* once for the tree */
|
|
|
|
btrfs_put_ordered_extent(ordered_extent);
|
|
|
|
|
|
|
|
btrfs_end_transaction(trans, root);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-12-02 22:54:17 +08:00
|
|
|
static int btrfs_writepage_end_io_hook(struct page *page, u64 start, u64 end,
|
2008-07-18 23:56:15 +08:00
|
|
|
struct extent_state *state, int uptodate)
|
|
|
|
{
|
2009-09-03 04:53:46 +08:00
|
|
|
ClearPagePrivate2(page);
|
2008-07-18 23:56:15 +08:00
|
|
|
return btrfs_finish_ordered_io(page->mapping->host, start, end);
|
|
|
|
}
|
|
|
|
|
2008-09-30 03:18:18 +08:00
|
|
|
/*
|
|
|
|
* When IO fails, either with EIO or csum verification fails, we
|
|
|
|
* try other mirrors that might have a good copy of the data. This
|
|
|
|
* io_failure_record is used to record state as we go through all the
|
|
|
|
* mirrors. If another mirror has good data, the page is set up to date
|
|
|
|
* and things continue. If a good mirror can't be found, the original
|
|
|
|
* bio end_io callback is called to indicate things have failed.
|
|
|
|
*/
|
2008-04-10 04:28:12 +08:00
|
|
|
struct io_failure_record {
|
|
|
|
struct page *page;
|
|
|
|
u64 start;
|
|
|
|
u64 len;
|
|
|
|
u64 logical;
|
Btrfs: move data checksumming into a dedicated tree
Btrfs stores checksums for each data block. Until now, they have
been stored in the subvolume trees, indexed by the inode that is
referencing the data block. This means that when we read the inode,
we've probably read in at least some checksums as well.
But, this has a few problems:
* The checksums are indexed by logical offset in the file. When
compression is on, this means we have to do the expensive checksumming
on the uncompressed data. It would be faster if we could checksum
the compressed data instead.
* If we implement encryption, we'll be checksumming the plain text and
storing that on disk. This is significantly less secure.
* For either compression or encryption, we have to get the plain text
back before we can verify the checksum as correct. This makes the raid
layer balancing and extent moving much more expensive.
* It makes the front end caching code more complex, as we have touch
the subvolume and inodes as we cache extents.
* There is potentitally one copy of the checksum in each subvolume
referencing an extent.
The solution used here is to store the extent checksums in a dedicated
tree. This allows us to index the checksums by phyiscal extent
start and length. It means:
* The checksum is against the data stored on disk, after any compression
or encryption is done.
* The checksum is stored in a central location, and can be verified without
following back references, or reading inodes.
This makes compression significantly faster by reducing the amount of
data that needs to be checksummed. It will also allow much faster
raid management code in general.
The checksums are indexed by a key with a fixed objectid (a magic value
in ctree.h) and offset set to the starting byte of the extent. This
allows us to copy the checksum items into the fsync log tree directly (or
any other tree), without having to invent a second format for them.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-12-09 05:58:54 +08:00
|
|
|
unsigned long bio_flags;
|
2008-04-10 04:28:12 +08:00
|
|
|
int last_mirror;
|
|
|
|
};
|
|
|
|
|
2008-12-02 22:54:17 +08:00
|
|
|
static int btrfs_io_failed_hook(struct bio *failed_bio,
|
2008-05-13 01:39:03 +08:00
|
|
|
struct page *page, u64 start, u64 end,
|
|
|
|
struct extent_state *state)
|
2008-04-10 04:28:12 +08:00
|
|
|
{
|
|
|
|
struct io_failure_record *failrec = NULL;
|
|
|
|
u64 private;
|
|
|
|
struct extent_map *em;
|
|
|
|
struct inode *inode = page->mapping->host;
|
|
|
|
struct extent_io_tree *failure_tree = &BTRFS_I(inode)->io_failure_tree;
|
2008-04-17 23:29:12 +08:00
|
|
|
struct extent_map_tree *em_tree = &BTRFS_I(inode)->extent_tree;
|
2008-04-10 04:28:12 +08:00
|
|
|
struct bio *bio;
|
|
|
|
int num_copies;
|
|
|
|
int ret;
|
2008-05-13 01:39:03 +08:00
|
|
|
int rw;
|
2008-04-10 04:28:12 +08:00
|
|
|
u64 logical;
|
|
|
|
|
|
|
|
ret = get_state_private(failure_tree, start, &private);
|
|
|
|
if (ret) {
|
|
|
|
failrec = kmalloc(sizeof(*failrec), GFP_NOFS);
|
|
|
|
if (!failrec)
|
|
|
|
return -ENOMEM;
|
|
|
|
failrec->start = start;
|
|
|
|
failrec->len = end - start + 1;
|
|
|
|
failrec->last_mirror = 0;
|
Btrfs: move data checksumming into a dedicated tree
Btrfs stores checksums for each data block. Until now, they have
been stored in the subvolume trees, indexed by the inode that is
referencing the data block. This means that when we read the inode,
we've probably read in at least some checksums as well.
But, this has a few problems:
* The checksums are indexed by logical offset in the file. When
compression is on, this means we have to do the expensive checksumming
on the uncompressed data. It would be faster if we could checksum
the compressed data instead.
* If we implement encryption, we'll be checksumming the plain text and
storing that on disk. This is significantly less secure.
* For either compression or encryption, we have to get the plain text
back before we can verify the checksum as correct. This makes the raid
layer balancing and extent moving much more expensive.
* It makes the front end caching code more complex, as we have touch
the subvolume and inodes as we cache extents.
* There is potentitally one copy of the checksum in each subvolume
referencing an extent.
The solution used here is to store the extent checksums in a dedicated
tree. This allows us to index the checksums by phyiscal extent
start and length. It means:
* The checksum is against the data stored on disk, after any compression
or encryption is done.
* The checksum is stored in a central location, and can be verified without
following back references, or reading inodes.
This makes compression significantly faster by reducing the amount of
data that needs to be checksummed. It will also allow much faster
raid management code in general.
The checksums are indexed by a key with a fixed objectid (a magic value
in ctree.h) and offset set to the starting byte of the extent. This
allows us to copy the checksum items into the fsync log tree directly (or
any other tree), without having to invent a second format for them.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-12-09 05:58:54 +08:00
|
|
|
failrec->bio_flags = 0;
|
2008-04-10 04:28:12 +08:00
|
|
|
|
2009-09-03 04:24:52 +08:00
|
|
|
read_lock(&em_tree->lock);
|
2008-04-17 23:29:12 +08:00
|
|
|
em = lookup_extent_mapping(em_tree, start, failrec->len);
|
|
|
|
if (em->start > start || em->start + em->len < start) {
|
|
|
|
free_extent_map(em);
|
|
|
|
em = NULL;
|
|
|
|
}
|
2009-09-03 04:24:52 +08:00
|
|
|
read_unlock(&em_tree->lock);
|
2008-04-10 04:28:12 +08:00
|
|
|
|
|
|
|
if (!em || IS_ERR(em)) {
|
|
|
|
kfree(failrec);
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
logical = start - em->start;
|
|
|
|
logical = em->block_start + logical;
|
Btrfs: move data checksumming into a dedicated tree
Btrfs stores checksums for each data block. Until now, they have
been stored in the subvolume trees, indexed by the inode that is
referencing the data block. This means that when we read the inode,
we've probably read in at least some checksums as well.
But, this has a few problems:
* The checksums are indexed by logical offset in the file. When
compression is on, this means we have to do the expensive checksumming
on the uncompressed data. It would be faster if we could checksum
the compressed data instead.
* If we implement encryption, we'll be checksumming the plain text and
storing that on disk. This is significantly less secure.
* For either compression or encryption, we have to get the plain text
back before we can verify the checksum as correct. This makes the raid
layer balancing and extent moving much more expensive.
* It makes the front end caching code more complex, as we have touch
the subvolume and inodes as we cache extents.
* There is potentitally one copy of the checksum in each subvolume
referencing an extent.
The solution used here is to store the extent checksums in a dedicated
tree. This allows us to index the checksums by phyiscal extent
start and length. It means:
* The checksum is against the data stored on disk, after any compression
or encryption is done.
* The checksum is stored in a central location, and can be verified without
following back references, or reading inodes.
This makes compression significantly faster by reducing the amount of
data that needs to be checksummed. It will also allow much faster
raid management code in general.
The checksums are indexed by a key with a fixed objectid (a magic value
in ctree.h) and offset set to the starting byte of the extent. This
allows us to copy the checksum items into the fsync log tree directly (or
any other tree), without having to invent a second format for them.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-12-09 05:58:54 +08:00
|
|
|
if (test_bit(EXTENT_FLAG_COMPRESSED, &em->flags)) {
|
|
|
|
logical = em->block_start;
|
|
|
|
failrec->bio_flags = EXTENT_BIO_COMPRESSED;
|
|
|
|
}
|
2008-04-10 04:28:12 +08:00
|
|
|
failrec->logical = logical;
|
|
|
|
free_extent_map(em);
|
|
|
|
set_extent_bits(failure_tree, start, end, EXTENT_LOCKED |
|
|
|
|
EXTENT_DIRTY, GFP_NOFS);
|
2008-04-12 00:16:46 +08:00
|
|
|
set_state_private(failure_tree, start,
|
|
|
|
(u64)(unsigned long)failrec);
|
2008-04-10 04:28:12 +08:00
|
|
|
} else {
|
2008-04-12 00:16:46 +08:00
|
|
|
failrec = (struct io_failure_record *)(unsigned long)private;
|
2008-04-10 04:28:12 +08:00
|
|
|
}
|
|
|
|
num_copies = btrfs_num_copies(
|
|
|
|
&BTRFS_I(inode)->root->fs_info->mapping_tree,
|
|
|
|
failrec->logical, failrec->len);
|
|
|
|
failrec->last_mirror++;
|
|
|
|
if (!state) {
|
2008-12-18 03:51:42 +08:00
|
|
|
spin_lock(&BTRFS_I(inode)->io_tree.lock);
|
2008-04-10 04:28:12 +08:00
|
|
|
state = find_first_extent_bit_state(&BTRFS_I(inode)->io_tree,
|
|
|
|
failrec->start,
|
|
|
|
EXTENT_LOCKED);
|
|
|
|
if (state && state->start != failrec->start)
|
|
|
|
state = NULL;
|
2008-12-18 03:51:42 +08:00
|
|
|
spin_unlock(&BTRFS_I(inode)->io_tree.lock);
|
2008-04-10 04:28:12 +08:00
|
|
|
}
|
|
|
|
if (!state || failrec->last_mirror > num_copies) {
|
|
|
|
set_state_private(failure_tree, failrec->start, 0);
|
|
|
|
clear_extent_bits(failure_tree, failrec->start,
|
|
|
|
failrec->start + failrec->len - 1,
|
|
|
|
EXTENT_LOCKED | EXTENT_DIRTY, GFP_NOFS);
|
|
|
|
kfree(failrec);
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
bio = bio_alloc(GFP_NOFS, 1);
|
|
|
|
bio->bi_private = state;
|
|
|
|
bio->bi_end_io = failed_bio->bi_end_io;
|
|
|
|
bio->bi_sector = failrec->logical >> 9;
|
|
|
|
bio->bi_bdev = failed_bio->bi_bdev;
|
2008-04-23 01:26:46 +08:00
|
|
|
bio->bi_size = 0;
|
Btrfs: move data checksumming into a dedicated tree
Btrfs stores checksums for each data block. Until now, they have
been stored in the subvolume trees, indexed by the inode that is
referencing the data block. This means that when we read the inode,
we've probably read in at least some checksums as well.
But, this has a few problems:
* The checksums are indexed by logical offset in the file. When
compression is on, this means we have to do the expensive checksumming
on the uncompressed data. It would be faster if we could checksum
the compressed data instead.
* If we implement encryption, we'll be checksumming the plain text and
storing that on disk. This is significantly less secure.
* For either compression or encryption, we have to get the plain text
back before we can verify the checksum as correct. This makes the raid
layer balancing and extent moving much more expensive.
* It makes the front end caching code more complex, as we have touch
the subvolume and inodes as we cache extents.
* There is potentitally one copy of the checksum in each subvolume
referencing an extent.
The solution used here is to store the extent checksums in a dedicated
tree. This allows us to index the checksums by phyiscal extent
start and length. It means:
* The checksum is against the data stored on disk, after any compression
or encryption is done.
* The checksum is stored in a central location, and can be verified without
following back references, or reading inodes.
This makes compression significantly faster by reducing the amount of
data that needs to be checksummed. It will also allow much faster
raid management code in general.
The checksums are indexed by a key with a fixed objectid (a magic value
in ctree.h) and offset set to the starting byte of the extent. This
allows us to copy the checksum items into the fsync log tree directly (or
any other tree), without having to invent a second format for them.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-12-09 05:58:54 +08:00
|
|
|
|
2008-04-10 04:28:12 +08:00
|
|
|
bio_add_page(bio, page, failrec->len, start - page_offset(page));
|
2008-05-13 01:39:03 +08:00
|
|
|
if (failed_bio->bi_rw & (1 << BIO_RW))
|
|
|
|
rw = WRITE;
|
|
|
|
else
|
|
|
|
rw = READ;
|
|
|
|
|
|
|
|
BTRFS_I(inode)->io_tree.ops->submit_bio_hook(inode, rw, bio,
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
failrec->last_mirror,
|
Btrfs: move data checksumming into a dedicated tree
Btrfs stores checksums for each data block. Until now, they have
been stored in the subvolume trees, indexed by the inode that is
referencing the data block. This means that when we read the inode,
we've probably read in at least some checksums as well.
But, this has a few problems:
* The checksums are indexed by logical offset in the file. When
compression is on, this means we have to do the expensive checksumming
on the uncompressed data. It would be faster if we could checksum
the compressed data instead.
* If we implement encryption, we'll be checksumming the plain text and
storing that on disk. This is significantly less secure.
* For either compression or encryption, we have to get the plain text
back before we can verify the checksum as correct. This makes the raid
layer balancing and extent moving much more expensive.
* It makes the front end caching code more complex, as we have touch
the subvolume and inodes as we cache extents.
* There is potentitally one copy of the checksum in each subvolume
referencing an extent.
The solution used here is to store the extent checksums in a dedicated
tree. This allows us to index the checksums by phyiscal extent
start and length. It means:
* The checksum is against the data stored on disk, after any compression
or encryption is done.
* The checksum is stored in a central location, and can be verified without
following back references, or reading inodes.
This makes compression significantly faster by reducing the amount of
data that needs to be checksummed. It will also allow much faster
raid management code in general.
The checksums are indexed by a key with a fixed objectid (a magic value
in ctree.h) and offset set to the starting byte of the extent. This
allows us to copy the checksum items into the fsync log tree directly (or
any other tree), without having to invent a second format for them.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-12-09 05:58:54 +08:00
|
|
|
failrec->bio_flags);
|
2008-05-13 01:39:03 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-09-30 03:18:18 +08:00
|
|
|
/*
|
|
|
|
* each time an IO finishes, we do a fast check in the IO failure tree
|
|
|
|
* to see if we need to process or clean up an io_failure_record
|
|
|
|
*/
|
2008-12-02 22:54:17 +08:00
|
|
|
static int btrfs_clean_io_failures(struct inode *inode, u64 start)
|
2008-05-13 01:39:03 +08:00
|
|
|
{
|
|
|
|
u64 private;
|
|
|
|
u64 private_failure;
|
|
|
|
struct io_failure_record *failure;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
private = 0;
|
|
|
|
if (count_range_bits(&BTRFS_I(inode)->io_failure_tree, &private,
|
|
|
|
(u64)-1, 1, EXTENT_DIRTY)) {
|
|
|
|
ret = get_state_private(&BTRFS_I(inode)->io_failure_tree,
|
|
|
|
start, &private_failure);
|
|
|
|
if (ret == 0) {
|
|
|
|
failure = (struct io_failure_record *)(unsigned long)
|
|
|
|
private_failure;
|
|
|
|
set_state_private(&BTRFS_I(inode)->io_failure_tree,
|
|
|
|
failure->start, 0);
|
|
|
|
clear_extent_bits(&BTRFS_I(inode)->io_failure_tree,
|
|
|
|
failure->start,
|
|
|
|
failure->start + failure->len - 1,
|
|
|
|
EXTENT_DIRTY | EXTENT_LOCKED,
|
|
|
|
GFP_NOFS);
|
|
|
|
kfree(failure);
|
|
|
|
}
|
|
|
|
}
|
2008-04-10 04:28:12 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-09-30 03:18:18 +08:00
|
|
|
/*
|
|
|
|
* when reads are done, we need to check csums to verify the data is correct
|
|
|
|
* if there's a match, we allow the bio to finish. If not, we go through
|
|
|
|
* the io_failure_record routines to find good copies
|
|
|
|
*/
|
2008-12-02 22:54:17 +08:00
|
|
|
static int btrfs_readpage_end_io_hook(struct page *page, u64 start, u64 end,
|
2008-01-29 22:59:12 +08:00
|
|
|
struct extent_state *state)
|
2007-08-30 20:50:51 +08:00
|
|
|
{
|
2007-10-31 04:56:53 +08:00
|
|
|
size_t offset = start - ((u64)page->index << PAGE_CACHE_SHIFT);
|
2007-08-30 20:50:51 +08:00
|
|
|
struct inode *inode = page->mapping->host;
|
2008-01-25 05:13:08 +08:00
|
|
|
struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree;
|
2007-08-30 20:50:51 +08:00
|
|
|
char *kaddr;
|
2008-01-29 22:10:27 +08:00
|
|
|
u64 private = ~(u32)0;
|
2007-08-30 20:50:51 +08:00
|
|
|
int ret;
|
2007-10-16 04:22:25 +08:00
|
|
|
struct btrfs_root *root = BTRFS_I(inode)->root;
|
|
|
|
u32 csum = ~(u32)0;
|
2008-01-25 05:13:08 +08:00
|
|
|
|
Btrfs: move data checksumming into a dedicated tree
Btrfs stores checksums for each data block. Until now, they have
been stored in the subvolume trees, indexed by the inode that is
referencing the data block. This means that when we read the inode,
we've probably read in at least some checksums as well.
But, this has a few problems:
* The checksums are indexed by logical offset in the file. When
compression is on, this means we have to do the expensive checksumming
on the uncompressed data. It would be faster if we could checksum
the compressed data instead.
* If we implement encryption, we'll be checksumming the plain text and
storing that on disk. This is significantly less secure.
* For either compression or encryption, we have to get the plain text
back before we can verify the checksum as correct. This makes the raid
layer balancing and extent moving much more expensive.
* It makes the front end caching code more complex, as we have touch
the subvolume and inodes as we cache extents.
* There is potentitally one copy of the checksum in each subvolume
referencing an extent.
The solution used here is to store the extent checksums in a dedicated
tree. This allows us to index the checksums by phyiscal extent
start and length. It means:
* The checksum is against the data stored on disk, after any compression
or encryption is done.
* The checksum is stored in a central location, and can be verified without
following back references, or reading inodes.
This makes compression significantly faster by reducing the amount of
data that needs to be checksummed. It will also allow much faster
raid management code in general.
The checksums are indexed by a key with a fixed objectid (a magic value
in ctree.h) and offset set to the starting byte of the extent. This
allows us to copy the checksum items into the fsync log tree directly (or
any other tree), without having to invent a second format for them.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-12-09 05:58:54 +08:00
|
|
|
if (PageChecked(page)) {
|
|
|
|
ClearPageChecked(page);
|
|
|
|
goto good;
|
|
|
|
}
|
2009-04-17 16:37:41 +08:00
|
|
|
|
|
|
|
if (BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM)
|
2008-12-12 23:03:38 +08:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (root->root_key.objectid == BTRFS_DATA_RELOC_TREE_OBJECTID &&
|
2009-09-03 03:22:30 +08:00
|
|
|
test_range_bit(io_tree, start, end, EXTENT_NODATASUM, 1, NULL)) {
|
2008-12-12 23:03:38 +08:00
|
|
|
clear_extent_bits(io_tree, start, end, EXTENT_NODATASUM,
|
|
|
|
GFP_NOFS);
|
2007-12-15 04:30:32 +08:00
|
|
|
return 0;
|
2008-12-12 23:03:38 +08:00
|
|
|
}
|
Btrfs: move data checksumming into a dedicated tree
Btrfs stores checksums for each data block. Until now, they have
been stored in the subvolume trees, indexed by the inode that is
referencing the data block. This means that when we read the inode,
we've probably read in at least some checksums as well.
But, this has a few problems:
* The checksums are indexed by logical offset in the file. When
compression is on, this means we have to do the expensive checksumming
on the uncompressed data. It would be faster if we could checksum
the compressed data instead.
* If we implement encryption, we'll be checksumming the plain text and
storing that on disk. This is significantly less secure.
* For either compression or encryption, we have to get the plain text
back before we can verify the checksum as correct. This makes the raid
layer balancing and extent moving much more expensive.
* It makes the front end caching code more complex, as we have touch
the subvolume and inodes as we cache extents.
* There is potentitally one copy of the checksum in each subvolume
referencing an extent.
The solution used here is to store the extent checksums in a dedicated
tree. This allows us to index the checksums by phyiscal extent
start and length. It means:
* The checksum is against the data stored on disk, after any compression
or encryption is done.
* The checksum is stored in a central location, and can be verified without
following back references, or reading inodes.
This makes compression significantly faster by reducing the amount of
data that needs to be checksummed. It will also allow much faster
raid management code in general.
The checksums are indexed by a key with a fixed objectid (a magic value
in ctree.h) and offset set to the starting byte of the extent. This
allows us to copy the checksum items into the fsync log tree directly (or
any other tree), without having to invent a second format for them.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-12-09 05:58:54 +08:00
|
|
|
|
2008-02-04 21:57:25 +08:00
|
|
|
if (state && state->start == start) {
|
2008-01-29 22:59:12 +08:00
|
|
|
private = state->private;
|
|
|
|
ret = 0;
|
|
|
|
} else {
|
|
|
|
ret = get_state_private(io_tree, start, &private);
|
|
|
|
}
|
2009-01-07 22:48:51 +08:00
|
|
|
kaddr = kmap_atomic(page, KM_USER0);
|
2009-01-06 10:25:51 +08:00
|
|
|
if (ret)
|
2007-08-30 20:50:51 +08:00
|
|
|
goto zeroit;
|
2009-01-06 10:25:51 +08:00
|
|
|
|
2007-10-16 04:22:25 +08:00
|
|
|
csum = btrfs_csum_data(root, kaddr + offset, csum, end - start + 1);
|
|
|
|
btrfs_csum_final(csum, (char *)&csum);
|
2009-01-06 10:25:51 +08:00
|
|
|
if (csum != private)
|
2007-08-30 20:50:51 +08:00
|
|
|
goto zeroit;
|
2009-01-06 10:25:51 +08:00
|
|
|
|
2009-01-07 22:48:51 +08:00
|
|
|
kunmap_atomic(kaddr, KM_USER0);
|
Btrfs: move data checksumming into a dedicated tree
Btrfs stores checksums for each data block. Until now, they have
been stored in the subvolume trees, indexed by the inode that is
referencing the data block. This means that when we read the inode,
we've probably read in at least some checksums as well.
But, this has a few problems:
* The checksums are indexed by logical offset in the file. When
compression is on, this means we have to do the expensive checksumming
on the uncompressed data. It would be faster if we could checksum
the compressed data instead.
* If we implement encryption, we'll be checksumming the plain text and
storing that on disk. This is significantly less secure.
* For either compression or encryption, we have to get the plain text
back before we can verify the checksum as correct. This makes the raid
layer balancing and extent moving much more expensive.
* It makes the front end caching code more complex, as we have touch
the subvolume and inodes as we cache extents.
* There is potentitally one copy of the checksum in each subvolume
referencing an extent.
The solution used here is to store the extent checksums in a dedicated
tree. This allows us to index the checksums by phyiscal extent
start and length. It means:
* The checksum is against the data stored on disk, after any compression
or encryption is done.
* The checksum is stored in a central location, and can be verified without
following back references, or reading inodes.
This makes compression significantly faster by reducing the amount of
data that needs to be checksummed. It will also allow much faster
raid management code in general.
The checksums are indexed by a key with a fixed objectid (a magic value
in ctree.h) and offset set to the starting byte of the extent. This
allows us to copy the checksum items into the fsync log tree directly (or
any other tree), without having to invent a second format for them.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-12-09 05:58:54 +08:00
|
|
|
good:
|
2008-04-10 04:28:12 +08:00
|
|
|
/* if the io failure tree for this inode is non-empty,
|
|
|
|
* check to see if we've recovered from a failed IO
|
|
|
|
*/
|
2008-05-13 01:39:03 +08:00
|
|
|
btrfs_clean_io_failures(inode, start);
|
2007-08-30 20:50:51 +08:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
zeroit:
|
2009-04-27 19:29:05 +08:00
|
|
|
if (printk_ratelimit()) {
|
|
|
|
printk(KERN_INFO "btrfs csum failed ino %lu off %llu csum %u "
|
|
|
|
"private %llu\n", page->mapping->host->i_ino,
|
|
|
|
(unsigned long long)start, csum,
|
|
|
|
(unsigned long long)private);
|
|
|
|
}
|
2007-10-16 04:15:53 +08:00
|
|
|
memset(kaddr + offset, 1, end - start + 1);
|
|
|
|
flush_dcache_page(page);
|
2009-01-07 22:48:51 +08:00
|
|
|
kunmap_atomic(kaddr, KM_USER0);
|
2008-04-17 23:29:12 +08:00
|
|
|
if (private == 0)
|
|
|
|
return 0;
|
2008-04-10 04:28:12 +08:00
|
|
|
return -EIO;
|
2007-08-30 20:50:51 +08:00
|
|
|
}
|
2007-08-28 04:49:44 +08:00
|
|
|
|
2008-07-25 00:17:14 +08:00
|
|
|
/*
|
|
|
|
* This creates an orphan entry for the given inode in case something goes
|
|
|
|
* wrong in the middle of an unlink/truncate.
|
|
|
|
*/
|
|
|
|
int btrfs_orphan_add(struct btrfs_trans_handle *trans, struct inode *inode)
|
|
|
|
{
|
|
|
|
struct btrfs_root *root = BTRFS_I(inode)->root;
|
|
|
|
int ret = 0;
|
|
|
|
|
2008-07-31 04:29:20 +08:00
|
|
|
spin_lock(&root->list_lock);
|
2008-07-25 00:17:14 +08:00
|
|
|
|
|
|
|
/* already on the orphan list, we're good */
|
|
|
|
if (!list_empty(&BTRFS_I(inode)->i_orphan)) {
|
2008-07-31 04:29:20 +08:00
|
|
|
spin_unlock(&root->list_lock);
|
2008-07-25 00:17:14 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
list_add(&BTRFS_I(inode)->i_orphan, &root->orphan_list);
|
|
|
|
|
2008-07-31 04:29:20 +08:00
|
|
|
spin_unlock(&root->list_lock);
|
2008-07-25 00:17:14 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* insert an orphan item to track this unlinked/truncated file
|
|
|
|
*/
|
|
|
|
ret = btrfs_insert_orphan_item(trans, root, inode->i_ino);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We have done the truncate/delete so we can go ahead and remove the orphan
|
|
|
|
* item for this particular inode.
|
|
|
|
*/
|
|
|
|
int btrfs_orphan_del(struct btrfs_trans_handle *trans, struct inode *inode)
|
|
|
|
{
|
|
|
|
struct btrfs_root *root = BTRFS_I(inode)->root;
|
|
|
|
int ret = 0;
|
|
|
|
|
2008-07-31 04:29:20 +08:00
|
|
|
spin_lock(&root->list_lock);
|
2008-07-25 00:17:14 +08:00
|
|
|
|
|
|
|
if (list_empty(&BTRFS_I(inode)->i_orphan)) {
|
2008-07-31 04:29:20 +08:00
|
|
|
spin_unlock(&root->list_lock);
|
2008-07-25 00:17:14 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
list_del_init(&BTRFS_I(inode)->i_orphan);
|
|
|
|
if (!trans) {
|
2008-07-31 04:29:20 +08:00
|
|
|
spin_unlock(&root->list_lock);
|
2008-07-25 00:17:14 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-07-31 04:29:20 +08:00
|
|
|
spin_unlock(&root->list_lock);
|
2008-07-25 00:17:14 +08:00
|
|
|
|
|
|
|
ret = btrfs_del_orphan_item(trans, root, inode->i_ino);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* this cleans up any orphans that may be left on the list from the last use
|
|
|
|
* of this root.
|
|
|
|
*/
|
|
|
|
void btrfs_orphan_cleanup(struct btrfs_root *root)
|
|
|
|
{
|
|
|
|
struct btrfs_path *path;
|
|
|
|
struct extent_buffer *leaf;
|
|
|
|
struct btrfs_item *item;
|
|
|
|
struct btrfs_key key, found_key;
|
|
|
|
struct btrfs_trans_handle *trans;
|
|
|
|
struct inode *inode;
|
|
|
|
int ret = 0, nr_unlink = 0, nr_truncate = 0;
|
|
|
|
|
|
|
|
path = btrfs_alloc_path();
|
|
|
|
if (!path)
|
|
|
|
return;
|
|
|
|
path->reada = -1;
|
|
|
|
|
|
|
|
key.objectid = BTRFS_ORPHAN_OBJECTID;
|
|
|
|
btrfs_set_key_type(&key, BTRFS_ORPHAN_ITEM_KEY);
|
|
|
|
key.offset = (u64)-1;
|
|
|
|
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
|
|
|
|
if (ret < 0) {
|
|
|
|
printk(KERN_ERR "Error searching slot for orphan: %d"
|
|
|
|
"\n", ret);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* if ret == 0 means we found what we were searching for, which
|
|
|
|
* is weird, but possible, so only screw with path if we didnt
|
|
|
|
* find the key and see if we have stuff that matches
|
|
|
|
*/
|
|
|
|
if (ret > 0) {
|
|
|
|
if (path->slots[0] == 0)
|
|
|
|
break;
|
|
|
|
path->slots[0]--;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* pull out the item */
|
|
|
|
leaf = path->nodes[0];
|
|
|
|
item = btrfs_item_nr(leaf, path->slots[0]);
|
|
|
|
btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
|
|
|
|
|
|
|
|
/* make sure the item matches what we want */
|
|
|
|
if (found_key.objectid != BTRFS_ORPHAN_OBJECTID)
|
|
|
|
break;
|
|
|
|
if (btrfs_key_type(&found_key) != BTRFS_ORPHAN_ITEM_KEY)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* release the path since we're done with it */
|
|
|
|
btrfs_release_path(root, path);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* this is where we are basically btrfs_lookup, without the
|
|
|
|
* crossing root thing. we store the inode number in the
|
|
|
|
* offset of the orphan item.
|
|
|
|
*/
|
Btrfs: Mixed back reference (FORWARD ROLLING FORMAT CHANGE)
This commit introduces a new kind of back reference for btrfs metadata.
Once a filesystem has been mounted with this commit, IT WILL NO LONGER
BE MOUNTABLE BY OLDER KERNELS.
When a tree block in subvolume tree is cow'd, the reference counts of all
extents it points to are increased by one. At transaction commit time,
the old root of the subvolume is recorded in a "dead root" data structure,
and the btree it points to is later walked, dropping reference counts
and freeing any blocks where the reference count goes to 0.
The increments done during cow and decrements done after commit cancel out,
and the walk is a very expensive way to go about freeing the blocks that
are no longer referenced by the new btree root. This commit reduces the
transaction overhead by avoiding the need for dead root records.
When a non-shared tree block is cow'd, we free the old block at once, and the
new block inherits old block's references. When a tree block with reference
count > 1 is cow'd, we increase the reference counts of all extents
the new block points to by one, and decrease the old block's reference count by
one.
This dead tree avoidance code removes the need to modify the reference
counts of lower level extents when a non-shared tree block is cow'd.
But we still need to update back ref for all pointers in the block.
This is because the location of the block is recorded in the back ref
item.
We can solve this by introducing a new type of back ref. The new
back ref provides information about pointer's key, level and in which
tree the pointer lives. This information allow us to find the pointer
by searching the tree. The shortcoming of the new back ref is that it
only works for pointers in tree blocks referenced by their owner trees.
This is mostly a problem for snapshots, where resolving one of these
fuzzy back references would be O(number_of_snapshots) and quite slow.
The solution used here is to use the fuzzy back references in the common
case where a given tree block is only referenced by one root,
and use the full back references when multiple roots have a reference
on a given block.
This commit adds per subvolume red-black tree to keep trace of cached
inodes. The red-black tree helps the balancing code to find cached
inodes whose inode numbers within a given range.
This commit improves the balancing code by introducing several data
structures to keep the state of balancing. The most important one
is the back ref cache. It caches how the upper level tree blocks are
referenced. This greatly reduce the overhead of checking back ref.
The improved balancing code scales significantly better with a large
number of snapshots.
This is a very large commit and was written in a number of
pieces. But, they depend heavily on the disk format change and were
squashed together to make sure git bisect didn't end up in a
bad state wrt space balancing or the format change.
Signed-off-by: Yan Zheng <zheng.yan@oracle.com>
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2009-06-10 22:45:14 +08:00
|
|
|
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))
|
2008-07-25 00:17:14 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* add this inode to the orphan list so btrfs_orphan_del does
|
|
|
|
* the proper thing when we hit it
|
|
|
|
*/
|
2008-07-31 04:29:20 +08:00
|
|
|
spin_lock(&root->list_lock);
|
2008-07-25 00:17:14 +08:00
|
|
|
list_add(&BTRFS_I(inode)->i_orphan, &root->orphan_list);
|
2008-07-31 04:29:20 +08:00
|
|
|
spin_unlock(&root->list_lock);
|
2008-07-25 00:17:14 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* if this is a bad inode, means we actually succeeded in
|
|
|
|
* removing the inode, but not the orphan record, which means
|
|
|
|
* we need to manually delete the orphan since iput will just
|
|
|
|
* do a destroy_inode
|
|
|
|
*/
|
|
|
|
if (is_bad_inode(inode)) {
|
2008-09-26 22:05:38 +08:00
|
|
|
trans = btrfs_start_transaction(root, 1);
|
2008-07-25 00:17:14 +08:00
|
|
|
btrfs_orphan_del(trans, inode);
|
2008-09-26 22:05:38 +08:00
|
|
|
btrfs_end_transaction(trans, root);
|
2008-07-25 00:17:14 +08:00
|
|
|
iput(inode);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* if we have links, this was a truncate, lets do that */
|
|
|
|
if (inode->i_nlink) {
|
|
|
|
nr_truncate++;
|
|
|
|
btrfs_truncate(inode);
|
|
|
|
} else {
|
|
|
|
nr_unlink++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* this will do delete_inode and everything for us */
|
|
|
|
iput(inode);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nr_unlink)
|
|
|
|
printk(KERN_INFO "btrfs: unlinked %d orphans\n", nr_unlink);
|
|
|
|
if (nr_truncate)
|
|
|
|
printk(KERN_INFO "btrfs: truncated %d orphans\n", nr_truncate);
|
|
|
|
|
|
|
|
btrfs_free_path(path);
|
|
|
|
}
|
|
|
|
|
2009-04-27 23:47:50 +08:00
|
|
|
/*
|
|
|
|
* very simple check to peek ahead in the leaf looking for xattrs. If we
|
|
|
|
* don't find any xattrs, we know there can't be any acls.
|
|
|
|
*
|
|
|
|
* slot is the slot the inode is in, objectid is the objectid of the inode
|
|
|
|
*/
|
|
|
|
static noinline int acls_after_inode_item(struct extent_buffer *leaf,
|
|
|
|
int slot, u64 objectid)
|
|
|
|
{
|
|
|
|
u32 nritems = btrfs_header_nritems(leaf);
|
|
|
|
struct btrfs_key found_key;
|
|
|
|
int scanned = 0;
|
|
|
|
|
|
|
|
slot++;
|
|
|
|
while (slot < nritems) {
|
|
|
|
btrfs_item_key_to_cpu(leaf, &found_key, slot);
|
|
|
|
|
|
|
|
/* we found a different objectid, there must not be acls */
|
|
|
|
if (found_key.objectid != objectid)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* we found an xattr, assume we've got an acl */
|
|
|
|
if (found_key.type == BTRFS_XATTR_ITEM_KEY)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* we found a key greater than an xattr key, there can't
|
|
|
|
* be any acls later on
|
|
|
|
*/
|
|
|
|
if (found_key.type > BTRFS_XATTR_ITEM_KEY)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
slot++;
|
|
|
|
scanned++;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* it goes inode, inode backrefs, xattrs, extents,
|
|
|
|
* so if there are a ton of hard links to an inode there can
|
|
|
|
* be a lot of backrefs. Don't waste time searching too hard,
|
|
|
|
* this is just an optimization
|
|
|
|
*/
|
|
|
|
if (scanned >= 8)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* we hit the end of the leaf before we found an xattr or
|
|
|
|
* something larger than an xattr. We have to assume the inode
|
|
|
|
* has acls
|
|
|
|
*/
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2008-09-30 03:18:18 +08:00
|
|
|
/*
|
|
|
|
* read an inode from the btree into the in-memory inode
|
|
|
|
*/
|
Btrfs: Mixed back reference (FORWARD ROLLING FORMAT CHANGE)
This commit introduces a new kind of back reference for btrfs metadata.
Once a filesystem has been mounted with this commit, IT WILL NO LONGER
BE MOUNTABLE BY OLDER KERNELS.
When a tree block in subvolume tree is cow'd, the reference counts of all
extents it points to are increased by one. At transaction commit time,
the old root of the subvolume is recorded in a "dead root" data structure,
and the btree it points to is later walked, dropping reference counts
and freeing any blocks where the reference count goes to 0.
The increments done during cow and decrements done after commit cancel out,
and the walk is a very expensive way to go about freeing the blocks that
are no longer referenced by the new btree root. This commit reduces the
transaction overhead by avoiding the need for dead root records.
When a non-shared tree block is cow'd, we free the old block at once, and the
new block inherits old block's references. When a tree block with reference
count > 1 is cow'd, we increase the reference counts of all extents
the new block points to by one, and decrease the old block's reference count by
one.
This dead tree avoidance code removes the need to modify the reference
counts of lower level extents when a non-shared tree block is cow'd.
But we still need to update back ref for all pointers in the block.
This is because the location of the block is recorded in the back ref
item.
We can solve this by introducing a new type of back ref. The new
back ref provides information about pointer's key, level and in which
tree the pointer lives. This information allow us to find the pointer
by searching the tree. The shortcoming of the new back ref is that it
only works for pointers in tree blocks referenced by their owner trees.
This is mostly a problem for snapshots, where resolving one of these
fuzzy back references would be O(number_of_snapshots) and quite slow.
The solution used here is to use the fuzzy back references in the common
case where a given tree block is only referenced by one root,
and use the full back references when multiple roots have a reference
on a given block.
This commit adds per subvolume red-black tree to keep trace of cached
inodes. The red-black tree helps the balancing code to find cached
inodes whose inode numbers within a given range.
This commit improves the balancing code by introducing several data
structures to keep the state of balancing. The most important one
is the back ref cache. It caches how the upper level tree blocks are
referenced. This greatly reduce the overhead of checking back ref.
The improved balancing code scales significantly better with a large
number of snapshots.
This is a very large commit and was written in a number of
pieces. But, they depend heavily on the disk format change and were
squashed together to make sure git bisect didn't end up in a
bad state wrt space balancing or the format change.
Signed-off-by: Yan Zheng <zheng.yan@oracle.com>
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2009-06-10 22:45:14 +08:00
|
|
|
static void btrfs_read_locked_inode(struct inode *inode)
|
2007-06-12 18:35:45 +08:00
|
|
|
{
|
|
|
|
struct btrfs_path *path;
|
2007-10-16 04:14:19 +08:00
|
|
|
struct extent_buffer *leaf;
|
2007-06-12 18:35:45 +08:00
|
|
|
struct btrfs_inode_item *inode_item;
|
2008-03-25 03:01:56 +08:00
|
|
|
struct btrfs_timespec *tspec;
|
2007-06-12 18:35:45 +08:00
|
|
|
struct btrfs_root *root = BTRFS_I(inode)->root;
|
|
|
|
struct btrfs_key location;
|
2009-04-27 23:47:50 +08:00
|
|
|
int maybe_acls;
|
2007-06-12 18:35:45 +08:00
|
|
|
u64 alloc_group_block;
|
2007-07-11 22:18:17 +08:00
|
|
|
u32 rdev;
|
2007-06-12 18:35:45 +08:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
path = btrfs_alloc_path();
|
|
|
|
BUG_ON(!path);
|
|
|
|
memcpy(&location, &BTRFS_I(inode)->location, sizeof(location));
|
2008-01-09 04:46:30 +08:00
|
|
|
|
2007-06-12 18:35:45 +08:00
|
|
|
ret = btrfs_lookup_inode(NULL, root, path, &location, 0);
|
2007-10-16 04:14:19 +08:00
|
|
|
if (ret)
|
2007-06-12 18:35:45 +08:00
|
|
|
goto make_bad;
|
|
|
|
|
2007-10-16 04:14:19 +08:00
|
|
|
leaf = path->nodes[0];
|
|
|
|
inode_item = btrfs_item_ptr(leaf, path->slots[0],
|
|
|
|
struct btrfs_inode_item);
|
|
|
|
|
|
|
|
inode->i_mode = btrfs_inode_mode(leaf, inode_item);
|
|
|
|
inode->i_nlink = btrfs_inode_nlink(leaf, inode_item);
|
|
|
|
inode->i_uid = btrfs_inode_uid(leaf, inode_item);
|
|
|
|
inode->i_gid = btrfs_inode_gid(leaf, inode_item);
|
2008-07-18 00:54:05 +08:00
|
|
|
btrfs_i_size_write(inode, btrfs_inode_size(leaf, inode_item));
|
2007-10-16 04:14:19 +08:00
|
|
|
|
|
|
|
tspec = btrfs_inode_atime(inode_item);
|
|
|
|
inode->i_atime.tv_sec = btrfs_timespec_sec(leaf, tspec);
|
|
|
|
inode->i_atime.tv_nsec = btrfs_timespec_nsec(leaf, tspec);
|
|
|
|
|
|
|
|
tspec = btrfs_inode_mtime(inode_item);
|
|
|
|
inode->i_mtime.tv_sec = btrfs_timespec_sec(leaf, tspec);
|
|
|
|
inode->i_mtime.tv_nsec = btrfs_timespec_nsec(leaf, tspec);
|
|
|
|
|
|
|
|
tspec = btrfs_inode_ctime(inode_item);
|
|
|
|
inode->i_ctime.tv_sec = btrfs_timespec_sec(leaf, tspec);
|
|
|
|
inode->i_ctime.tv_nsec = btrfs_timespec_nsec(leaf, tspec);
|
|
|
|
|
2008-10-09 23:46:29 +08:00
|
|
|
inode_set_bytes(inode, btrfs_inode_nbytes(leaf, inode_item));
|
2008-09-06 04:13:11 +08:00
|
|
|
BTRFS_I(inode)->generation = btrfs_inode_generation(leaf, inode_item);
|
2008-12-09 05:40:21 +08:00
|
|
|
BTRFS_I(inode)->sequence = btrfs_inode_sequence(leaf, inode_item);
|
2008-09-06 04:13:11 +08:00
|
|
|
inode->i_generation = BTRFS_I(inode)->generation;
|
2007-07-11 22:18:17 +08:00
|
|
|
inode->i_rdev = 0;
|
2007-10-16 04:14:19 +08:00
|
|
|
rdev = btrfs_inode_rdev(leaf, inode_item);
|
|
|
|
|
2008-07-25 00:12:38 +08:00
|
|
|
BTRFS_I(inode)->index_cnt = (u64)-1;
|
2008-12-12 05:30:39 +08:00
|
|
|
BTRFS_I(inode)->flags = btrfs_inode_flags(leaf, inode_item);
|
2008-07-25 00:12:38 +08:00
|
|
|
|
2007-10-16 04:14:19 +08:00
|
|
|
alloc_group_block = btrfs_inode_block_group(leaf, inode_item);
|
Btrfs: Change btree locking to use explicit blocking points
Most of the btrfs metadata operations can be protected by a spinlock,
but some operations still need to schedule.
So far, btrfs has been using a mutex along with a trylock loop,
most of the time it is able to avoid going for the full mutex, so
the trylock loop is a big performance gain.
This commit is step one for getting rid of the blocking locks entirely.
btrfs_tree_lock takes a spinlock, and the code explicitly switches
to a blocking lock when it starts an operation that can schedule.
We'll be able get rid of the blocking locks in smaller pieces over time.
Tracing allows us to find the most common cause of blocking, so we
can start with the hot spots first.
The basic idea is:
btrfs_tree_lock() returns with the spin lock held
btrfs_set_lock_blocking() sets the EXTENT_BUFFER_BLOCKING bit in
the extent buffer flags, and then drops the spin lock. The buffer is
still considered locked by all of the btrfs code.
If btrfs_tree_lock gets the spinlock but finds the blocking bit set, it drops
the spin lock and waits on a wait queue for the blocking bit to go away.
Much of the code that needs to set the blocking bit finishes without actually
blocking a good percentage of the time. So, an adaptive spin is still
used against the blocking bit to avoid very high context switch rates.
btrfs_clear_lock_blocking() clears the blocking bit and returns
with the spinlock held again.
btrfs_tree_unlock() can be called on either blocking or spinning locks,
it does the right thing based on the blocking bit.
ctree.c has a helper function to set/clear all the locked buffers in a
path as blocking.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2009-02-04 22:25:08 +08:00
|
|
|
|
2009-04-27 23:47:50 +08:00
|
|
|
/*
|
|
|
|
* try to precache a NULL acl entry for files that don't have
|
|
|
|
* any xattrs or acls
|
|
|
|
*/
|
|
|
|
maybe_acls = acls_after_inode_item(leaf, path->slots[0], inode->i_ino);
|
2009-06-25 04:58:48 +08:00
|
|
|
if (!maybe_acls)
|
|
|
|
cache_no_acl(inode);
|
2009-04-27 23:47:50 +08:00
|
|
|
|
2008-12-12 05:30:39 +08:00
|
|
|
BTRFS_I(inode)->block_group = btrfs_find_block_group(root, 0,
|
|
|
|
alloc_group_block, 0);
|
2007-06-12 18:35:45 +08:00
|
|
|
btrfs_free_path(path);
|
|
|
|
inode_item = NULL;
|
|
|
|
|
|
|
|
switch (inode->i_mode & S_IFMT) {
|
|
|
|
case S_IFREG:
|
|
|
|
inode->i_mapping->a_ops = &btrfs_aops;
|
2008-03-26 22:28:07 +08:00
|
|
|
inode->i_mapping->backing_dev_info = &root->fs_info->bdi;
|
2008-01-25 05:13:08 +08:00
|
|
|
BTRFS_I(inode)->io_tree.ops = &btrfs_extent_io_ops;
|
2007-06-12 18:35:45 +08:00
|
|
|
inode->i_fop = &btrfs_file_operations;
|
|
|
|
inode->i_op = &btrfs_file_inode_operations;
|
|
|
|
break;
|
|
|
|
case S_IFDIR:
|
|
|
|
inode->i_fop = &btrfs_dir_file_operations;
|
|
|
|
if (root == root->fs_info->tree_root)
|
|
|
|
inode->i_op = &btrfs_dir_ro_inode_operations;
|
|
|
|
else
|
|
|
|
inode->i_op = &btrfs_dir_inode_operations;
|
|
|
|
break;
|
|
|
|
case S_IFLNK:
|
|
|
|
inode->i_op = &btrfs_symlink_inode_operations;
|
|
|
|
inode->i_mapping->a_ops = &btrfs_symlink_aops;
|
2008-03-26 22:28:07 +08:00
|
|
|
inode->i_mapping->backing_dev_info = &root->fs_info->bdi;
|
2007-06-12 18:35:45 +08:00
|
|
|
break;
|
2007-07-11 22:18:17 +08:00
|
|
|
default:
|
2009-02-04 22:29:13 +08:00
|
|
|
inode->i_op = &btrfs_special_inode_operations;
|
2007-07-11 22:18:17 +08:00
|
|
|
init_special_inode(inode, inode->i_mode, rdev);
|
|
|
|
break;
|
2007-06-12 18:35:45 +08:00
|
|
|
}
|
2009-04-17 16:37:41 +08:00
|
|
|
|
|
|
|
btrfs_update_iflags(inode);
|
2007-06-12 18:35:45 +08:00
|
|
|
return;
|
|
|
|
|
|
|
|
make_bad:
|
|
|
|
btrfs_free_path(path);
|
|
|
|
make_bad_inode(inode);
|
|
|
|
}
|
|
|
|
|
2008-09-30 03:18:18 +08:00
|
|
|
/*
|
|
|
|
* given a leaf and an inode, copy the inode fields into the leaf
|
|
|
|
*/
|
2008-09-06 04:13:11 +08:00
|
|
|
static void fill_inode_item(struct btrfs_trans_handle *trans,
|
|
|
|
struct extent_buffer *leaf,
|
2007-10-16 04:14:19 +08:00
|
|
|
struct btrfs_inode_item *item,
|
2007-06-12 18:35:45 +08:00
|
|
|
struct inode *inode)
|
|
|
|
{
|
2007-10-16 04:14:19 +08:00
|
|
|
btrfs_set_inode_uid(leaf, item, inode->i_uid);
|
|
|
|
btrfs_set_inode_gid(leaf, item, inode->i_gid);
|
2008-07-18 00:54:05 +08:00
|
|
|
btrfs_set_inode_size(leaf, item, BTRFS_I(inode)->disk_i_size);
|
2007-10-16 04:14:19 +08:00
|
|
|
btrfs_set_inode_mode(leaf, item, inode->i_mode);
|
|
|
|
btrfs_set_inode_nlink(leaf, item, inode->i_nlink);
|
|
|
|
|
|
|
|
btrfs_set_timespec_sec(leaf, btrfs_inode_atime(item),
|
|
|
|
inode->i_atime.tv_sec);
|
|
|
|
btrfs_set_timespec_nsec(leaf, btrfs_inode_atime(item),
|
|
|
|
inode->i_atime.tv_nsec);
|
|
|
|
|
|
|
|
btrfs_set_timespec_sec(leaf, btrfs_inode_mtime(item),
|
|
|
|
inode->i_mtime.tv_sec);
|
|
|
|
btrfs_set_timespec_nsec(leaf, btrfs_inode_mtime(item),
|
|
|
|
inode->i_mtime.tv_nsec);
|
|
|
|
|
|
|
|
btrfs_set_timespec_sec(leaf, btrfs_inode_ctime(item),
|
|
|
|
inode->i_ctime.tv_sec);
|
|
|
|
btrfs_set_timespec_nsec(leaf, btrfs_inode_ctime(item),
|
|
|
|
inode->i_ctime.tv_nsec);
|
|
|
|
|
2008-10-09 23:46:29 +08:00
|
|
|
btrfs_set_inode_nbytes(leaf, item, inode_get_bytes(inode));
|
2008-09-06 04:13:11 +08:00
|
|
|
btrfs_set_inode_generation(leaf, item, BTRFS_I(inode)->generation);
|
2008-12-09 05:40:21 +08:00
|
|
|
btrfs_set_inode_sequence(leaf, item, BTRFS_I(inode)->sequence);
|
2008-09-06 04:13:11 +08:00
|
|
|
btrfs_set_inode_transid(leaf, item, trans->transid);
|
2007-10-16 04:14:19 +08:00
|
|
|
btrfs_set_inode_rdev(leaf, item, inode->i_rdev);
|
2008-01-09 04:54:37 +08:00
|
|
|
btrfs_set_inode_flags(leaf, item, BTRFS_I(inode)->flags);
|
2008-12-12 05:30:39 +08:00
|
|
|
btrfs_set_inode_block_group(leaf, item, BTRFS_I(inode)->block_group);
|
2007-06-12 18:35:45 +08:00
|
|
|
}
|
|
|
|
|
2008-09-30 03:18:18 +08:00
|
|
|
/*
|
|
|
|
* copy everything in the in-memory inode into the btree.
|
|
|
|
*/
|
2009-01-06 10:25:51 +08:00
|
|
|
noinline int btrfs_update_inode(struct btrfs_trans_handle *trans,
|
|
|
|
struct btrfs_root *root, struct inode *inode)
|
2007-06-12 18:35:45 +08:00
|
|
|
{
|
|
|
|
struct btrfs_inode_item *inode_item;
|
|
|
|
struct btrfs_path *path;
|
2007-10-16 04:14:19 +08:00
|
|
|
struct extent_buffer *leaf;
|
2007-06-12 18:35:45 +08:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
path = btrfs_alloc_path();
|
|
|
|
BUG_ON(!path);
|
2009-03-13 23:00:37 +08:00
|
|
|
path->leave_spinning = 1;
|
2007-06-12 18:35:45 +08:00
|
|
|
ret = btrfs_lookup_inode(trans, root, path,
|
|
|
|
&BTRFS_I(inode)->location, 1);
|
|
|
|
if (ret) {
|
|
|
|
if (ret > 0)
|
|
|
|
ret = -ENOENT;
|
|
|
|
goto failed;
|
|
|
|
}
|
|
|
|
|
Btrfs: Change btree locking to use explicit blocking points
Most of the btrfs metadata operations can be protected by a spinlock,
but some operations still need to schedule.
So far, btrfs has been using a mutex along with a trylock loop,
most of the time it is able to avoid going for the full mutex, so
the trylock loop is a big performance gain.
This commit is step one for getting rid of the blocking locks entirely.
btrfs_tree_lock takes a spinlock, and the code explicitly switches
to a blocking lock when it starts an operation that can schedule.
We'll be able get rid of the blocking locks in smaller pieces over time.
Tracing allows us to find the most common cause of blocking, so we
can start with the hot spots first.
The basic idea is:
btrfs_tree_lock() returns with the spin lock held
btrfs_set_lock_blocking() sets the EXTENT_BUFFER_BLOCKING bit in
the extent buffer flags, and then drops the spin lock. The buffer is
still considered locked by all of the btrfs code.
If btrfs_tree_lock gets the spinlock but finds the blocking bit set, it drops
the spin lock and waits on a wait queue for the blocking bit to go away.
Much of the code that needs to set the blocking bit finishes without actually
blocking a good percentage of the time. So, an adaptive spin is still
used against the blocking bit to avoid very high context switch rates.
btrfs_clear_lock_blocking() clears the blocking bit and returns
with the spinlock held again.
btrfs_tree_unlock() can be called on either blocking or spinning locks,
it does the right thing based on the blocking bit.
ctree.c has a helper function to set/clear all the locked buffers in a
path as blocking.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2009-02-04 22:25:08 +08:00
|
|
|
btrfs_unlock_up_safe(path, 1);
|
2007-10-16 04:14:19 +08:00
|
|
|
leaf = path->nodes[0];
|
|
|
|
inode_item = btrfs_item_ptr(leaf, path->slots[0],
|
2007-06-12 18:35:45 +08:00
|
|
|
struct btrfs_inode_item);
|
|
|
|
|
2008-09-06 04:13:11 +08:00
|
|
|
fill_inode_item(trans, leaf, inode_item, inode);
|
2007-10-16 04:14:19 +08:00
|
|
|
btrfs_mark_buffer_dirty(leaf);
|
2007-08-11 04:22:09 +08:00
|
|
|
btrfs_set_inode_last_trans(trans, inode);
|
2007-06-12 18:35:45 +08:00
|
|
|
ret = 0;
|
|
|
|
failed:
|
|
|
|
btrfs_free_path(path);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-09-30 03:18:18 +08:00
|
|
|
/*
|
|
|
|
* unlink helper that gets used here in inode.c and in the tree logging
|
|
|
|
* recovery code. It remove a link in a directory with a given name, and
|
|
|
|
* also drops the back refs in the inode to the directory
|
|
|
|
*/
|
2008-09-06 04:13:11 +08:00
|
|
|
int btrfs_unlink_inode(struct btrfs_trans_handle *trans,
|
|
|
|
struct btrfs_root *root,
|
|
|
|
struct inode *dir, struct inode *inode,
|
|
|
|
const char *name, int name_len)
|
2007-06-12 18:35:45 +08:00
|
|
|
{
|
|
|
|
struct btrfs_path *path;
|
|
|
|
int ret = 0;
|
2007-10-16 04:14:19 +08:00
|
|
|
struct extent_buffer *leaf;
|
2007-06-12 18:35:45 +08:00
|
|
|
struct btrfs_dir_item *di;
|
2007-10-16 04:14:19 +08:00
|
|
|
struct btrfs_key key;
|
2008-07-25 00:12:38 +08:00
|
|
|
u64 index;
|
2007-06-12 18:35:45 +08:00
|
|
|
|
|
|
|
path = btrfs_alloc_path();
|
2007-06-23 02:16:25 +08:00
|
|
|
if (!path) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2009-03-13 23:00:37 +08:00
|
|
|
path->leave_spinning = 1;
|
2007-06-12 18:35:45 +08:00
|
|
|
di = btrfs_lookup_dir_item(trans, root, path, dir->i_ino,
|
|
|
|
name, name_len, -1);
|
|
|
|
if (IS_ERR(di)) {
|
|
|
|
ret = PTR_ERR(di);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
if (!di) {
|
|
|
|
ret = -ENOENT;
|
|
|
|
goto err;
|
|
|
|
}
|
2007-10-16 04:14:19 +08:00
|
|
|
leaf = path->nodes[0];
|
|
|
|
btrfs_dir_item_key_to_cpu(leaf, di, &key);
|
2007-06-12 18:35:45 +08:00
|
|
|
ret = btrfs_delete_one_dir_name(trans, root, path, di);
|
2007-06-23 02:16:25 +08:00
|
|
|
if (ret)
|
|
|
|
goto err;
|
2007-06-12 18:35:45 +08:00
|
|
|
btrfs_release_path(root, path);
|
|
|
|
|
2008-07-25 00:12:38 +08:00
|
|
|
ret = btrfs_del_inode_ref(trans, root, name, name_len,
|
2008-09-06 04:13:11 +08:00
|
|
|
inode->i_ino,
|
|
|
|
dir->i_ino, &index);
|
2008-07-25 00:12:38 +08:00
|
|
|
if (ret) {
|
2009-01-06 10:25:51 +08:00
|
|
|
printk(KERN_INFO "btrfs failed to delete reference to %.*s, "
|
2008-07-25 00:12:38 +08:00
|
|
|
"inode %lu parent %lu\n", name_len, name,
|
2008-09-06 04:13:11 +08:00
|
|
|
inode->i_ino, dir->i_ino);
|
2008-07-25 00:12:38 +08:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2007-06-12 18:35:45 +08:00
|
|
|
di = btrfs_lookup_dir_index_item(trans, root, path, dir->i_ino,
|
2008-07-25 00:12:38 +08:00
|
|
|
index, name, name_len, -1);
|
2007-06-12 18:35:45 +08:00
|
|
|
if (IS_ERR(di)) {
|
|
|
|
ret = PTR_ERR(di);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
if (!di) {
|
|
|
|
ret = -ENOENT;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
ret = btrfs_delete_one_dir_name(trans, root, path, di);
|
2008-06-26 04:01:30 +08:00
|
|
|
btrfs_release_path(root, path);
|
2007-06-12 18:35:45 +08:00
|
|
|
|
2008-09-06 04:13:11 +08:00
|
|
|
ret = btrfs_del_inode_ref_in_log(trans, root, name, name_len,
|
|
|
|
inode, dir->i_ino);
|
2008-09-12 03:53:12 +08:00
|
|
|
BUG_ON(ret != 0 && ret != -ENOENT);
|
2008-09-06 04:13:11 +08:00
|
|
|
|
|
|
|
ret = btrfs_del_dir_entries_in_log(trans, root, name, name_len,
|
|
|
|
dir, index);
|
|
|
|
BUG_ON(ret);
|
2007-06-12 18:35:45 +08:00
|
|
|
err:
|
|
|
|
btrfs_free_path(path);
|
2008-09-06 04:13:11 +08:00
|
|
|
if (ret)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
btrfs_i_size_write(dir, dir->i_size - name_len * 2);
|
|
|
|
inode->i_ctime = dir->i_mtime = dir->i_ctime = CURRENT_TIME;
|
|
|
|
btrfs_update_inode(trans, root, dir);
|
|
|
|
btrfs_drop_nlink(inode);
|
|
|
|
ret = btrfs_update_inode(trans, root, inode);
|
|
|
|
out:
|
2007-06-12 18:35:45 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int btrfs_unlink(struct inode *dir, struct dentry *dentry)
|
|
|
|
{
|
|
|
|
struct btrfs_root *root;
|
|
|
|
struct btrfs_trans_handle *trans;
|
2008-07-25 00:17:14 +08:00
|
|
|
struct inode *inode = dentry->d_inode;
|
2007-06-12 18:35:45 +08:00
|
|
|
int ret;
|
2007-12-22 05:27:21 +08:00
|
|
|
unsigned long nr = 0;
|
2007-06-12 18:35:45 +08:00
|
|
|
|
|
|
|
root = BTRFS_I(dir)->root;
|
2007-12-22 05:27:21 +08:00
|
|
|
|
2007-06-12 18:35:45 +08:00
|
|
|
trans = btrfs_start_transaction(root, 1);
|
2007-10-16 04:14:19 +08:00
|
|
|
|
2007-06-12 18:35:45 +08:00
|
|
|
btrfs_set_trans_block_group(trans, dir);
|
2009-03-24 22:24:20 +08:00
|
|
|
|
|
|
|
btrfs_record_unlink_dir(trans, dir, dentry->d_inode, 0);
|
|
|
|
|
2008-09-06 04:13:11 +08:00
|
|
|
ret = btrfs_unlink_inode(trans, root, dir, dentry->d_inode,
|
|
|
|
dentry->d_name.name, dentry->d_name.len);
|
2008-07-25 00:17:14 +08:00
|
|
|
|
|
|
|
if (inode->i_nlink == 0)
|
|
|
|
ret = btrfs_orphan_add(trans, inode);
|
|
|
|
|
2007-09-17 22:58:06 +08:00
|
|
|
nr = trans->blocks_used;
|
2007-10-16 04:14:19 +08:00
|
|
|
|
2008-06-26 04:01:31 +08:00
|
|
|
btrfs_end_transaction_throttle(trans, root);
|
2007-09-17 22:58:06 +08:00
|
|
|
btrfs_btree_balance_dirty(root, nr);
|
2007-06-12 18:35:45 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2009-09-22 03:56:00 +08:00
|
|
|
int btrfs_unlink_subvol(struct btrfs_trans_handle *trans,
|
|
|
|
struct btrfs_root *root,
|
|
|
|
struct inode *dir, u64 objectid,
|
|
|
|
const char *name, int name_len)
|
|
|
|
{
|
|
|
|
struct btrfs_path *path;
|
|
|
|
struct extent_buffer *leaf;
|
|
|
|
struct btrfs_dir_item *di;
|
|
|
|
struct btrfs_key key;
|
|
|
|
u64 index;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
path = btrfs_alloc_path();
|
|
|
|
if (!path)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
di = btrfs_lookup_dir_item(trans, root, path, dir->i_ino,
|
|
|
|
name, name_len, -1);
|
|
|
|
BUG_ON(!di || IS_ERR(di));
|
|
|
|
|
|
|
|
leaf = path->nodes[0];
|
|
|
|
btrfs_dir_item_key_to_cpu(leaf, di, &key);
|
|
|
|
WARN_ON(key.type != BTRFS_ROOT_ITEM_KEY || key.objectid != objectid);
|
|
|
|
ret = btrfs_delete_one_dir_name(trans, root, path, di);
|
|
|
|
BUG_ON(ret);
|
|
|
|
btrfs_release_path(root, path);
|
|
|
|
|
|
|
|
ret = btrfs_del_root_ref(trans, root->fs_info->tree_root,
|
|
|
|
objectid, root->root_key.objectid,
|
|
|
|
dir->i_ino, &index, name, name_len);
|
|
|
|
if (ret < 0) {
|
|
|
|
BUG_ON(ret != -ENOENT);
|
|
|
|
di = btrfs_search_dir_index_item(root, path, dir->i_ino,
|
|
|
|
name, name_len);
|
|
|
|
BUG_ON(!di || IS_ERR(di));
|
|
|
|
|
|
|
|
leaf = path->nodes[0];
|
|
|
|
btrfs_item_key_to_cpu(leaf, &key, path->slots[0]);
|
|
|
|
btrfs_release_path(root, path);
|
|
|
|
index = key.offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
di = btrfs_lookup_dir_index_item(trans, root, path, dir->i_ino,
|
|
|
|
index, name, name_len, -1);
|
|
|
|
BUG_ON(!di || IS_ERR(di));
|
|
|
|
|
|
|
|
leaf = path->nodes[0];
|
|
|
|
btrfs_dir_item_key_to_cpu(leaf, di, &key);
|
|
|
|
WARN_ON(key.type != BTRFS_ROOT_ITEM_KEY || key.objectid != objectid);
|
|
|
|
ret = btrfs_delete_one_dir_name(trans, root, path, di);
|
|
|
|
BUG_ON(ret);
|
|
|
|
btrfs_release_path(root, path);
|
|
|
|
|
|
|
|
btrfs_i_size_write(dir, dir->i_size - name_len * 2);
|
|
|
|
dir->i_mtime = dir->i_ctime = CURRENT_TIME;
|
|
|
|
ret = btrfs_update_inode(trans, root, dir);
|
|
|
|
BUG_ON(ret);
|
|
|
|
dir->i_sb->s_dirt = 1;
|
|
|
|
|
|
|
|
btrfs_free_path(path);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-06-12 18:35:45 +08:00
|
|
|
static int btrfs_rmdir(struct inode *dir, struct dentry *dentry)
|
|
|
|
{
|
|
|
|
struct inode *inode = dentry->d_inode;
|
2007-12-22 05:27:21 +08:00
|
|
|
int err = 0;
|
2007-06-12 18:35:45 +08:00
|
|
|
int ret;
|
|
|
|
struct btrfs_root *root = BTRFS_I(dir)->root;
|
|
|
|
struct btrfs_trans_handle *trans;
|
2007-12-22 05:27:21 +08:00
|
|
|
unsigned long nr = 0;
|
2007-06-12 18:35:45 +08:00
|
|
|
|
2008-11-18 09:42:26 +08:00
|
|
|
if (inode->i_size > BTRFS_EMPTY_DIR_SIZE ||
|
2009-09-22 03:56:00 +08:00
|
|
|
inode->i_ino == BTRFS_FIRST_FREE_OBJECTID)
|
2007-10-26 03:49:25 +08:00
|
|
|
return -ENOTEMPTY;
|
|
|
|
|
2007-06-12 18:35:45 +08:00
|
|
|
trans = btrfs_start_transaction(root, 1);
|
|
|
|
btrfs_set_trans_block_group(trans, dir);
|
|
|
|
|
2009-09-22 03:56:00 +08:00
|
|
|
if (unlikely(inode->i_ino == BTRFS_EMPTY_SUBVOL_DIR_OBJECTID)) {
|
|
|
|
err = btrfs_unlink_subvol(trans, root, dir,
|
|
|
|
BTRFS_I(inode)->location.objectid,
|
|
|
|
dentry->d_name.name,
|
|
|
|
dentry->d_name.len);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2008-07-25 00:17:14 +08:00
|
|
|
err = btrfs_orphan_add(trans, inode);
|
|
|
|
if (err)
|
2009-09-22 03:56:00 +08:00
|
|
|
goto out;
|
2008-07-25 00:17:14 +08:00
|
|
|
|
2007-06-12 18:35:45 +08:00
|
|
|
/* now the directory is empty */
|
2008-09-06 04:13:11 +08:00
|
|
|
err = btrfs_unlink_inode(trans, root, dir, dentry->d_inode,
|
|
|
|
dentry->d_name.name, dentry->d_name.len);
|
2009-01-06 10:25:51 +08:00
|
|
|
if (!err)
|
2008-07-18 00:54:05 +08:00
|
|
|
btrfs_i_size_write(inode, 0);
|
2009-09-22 03:56:00 +08:00
|
|
|
out:
|
2007-09-17 22:58:06 +08:00
|
|
|
nr = trans->blocks_used;
|
2008-06-26 04:01:31 +08:00
|
|
|
ret = btrfs_end_transaction_throttle(trans, root);
|
2007-09-17 22:58:06 +08:00
|
|
|
btrfs_btree_balance_dirty(root, nr);
|
2007-12-13 03:38:19 +08:00
|
|
|
|
2007-06-12 18:35:45 +08:00
|
|
|
if (ret && !err)
|
|
|
|
err = ret;
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
Btrfs: move data checksumming into a dedicated tree
Btrfs stores checksums for each data block. Until now, they have
been stored in the subvolume trees, indexed by the inode that is
referencing the data block. This means that when we read the inode,
we've probably read in at least some checksums as well.
But, this has a few problems:
* The checksums are indexed by logical offset in the file. When
compression is on, this means we have to do the expensive checksumming
on the uncompressed data. It would be faster if we could checksum
the compressed data instead.
* If we implement encryption, we'll be checksumming the plain text and
storing that on disk. This is significantly less secure.
* For either compression or encryption, we have to get the plain text
back before we can verify the checksum as correct. This makes the raid
layer balancing and extent moving much more expensive.
* It makes the front end caching code more complex, as we have touch
the subvolume and inodes as we cache extents.
* There is potentitally one copy of the checksum in each subvolume
referencing an extent.
The solution used here is to store the extent checksums in a dedicated
tree. This allows us to index the checksums by phyiscal extent
start and length. It means:
* The checksum is against the data stored on disk, after any compression
or encryption is done.
* The checksum is stored in a central location, and can be verified without
following back references, or reading inodes.
This makes compression significantly faster by reducing the amount of
data that needs to be checksummed. It will also allow much faster
raid management code in general.
The checksums are indexed by a key with a fixed objectid (a magic value
in ctree.h) and offset set to the starting byte of the extent. This
allows us to copy the checksum items into the fsync log tree directly (or
any other tree), without having to invent a second format for them.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-12-09 05:58:54 +08:00
|
|
|
#if 0
|
2008-10-02 07:05:46 +08:00
|
|
|
/*
|
|
|
|
* when truncating bytes in a file, it is possible to avoid reading
|
|
|
|
* the leaves that contain only checksum items. This can be the
|
|
|
|
* majority of the IO required to delete a large file, but it must
|
|
|
|
* be done carefully.
|
|
|
|
*
|
|
|
|
* The keys in the level just above the leaves are checked to make sure
|
|
|
|
* the lowest key in a given leaf is a csum key, and starts at an offset
|
|
|
|
* after the new size.
|
|
|
|
*
|
|
|
|
* Then the key for the next leaf is checked to make sure it also has
|
|
|
|
* a checksum item for the same file. If it does, we know our target leaf
|
|
|
|
* contains only checksum items, and it can be safely freed without reading
|
|
|
|
* it.
|
|
|
|
*
|
|
|
|
* This is just an optimization targeted at large files. It may do
|
|
|
|
* nothing. It will return 0 unless things went badly.
|
|
|
|
*/
|
|
|
|
static noinline int drop_csum_leaves(struct btrfs_trans_handle *trans,
|
|
|
|
struct btrfs_root *root,
|
|
|
|
struct btrfs_path *path,
|
|
|
|
struct inode *inode, u64 new_size)
|
|
|
|
{
|
|
|
|
struct btrfs_key key;
|
|
|
|
int ret;
|
|
|
|
int nritems;
|
|
|
|
struct btrfs_key found_key;
|
|
|
|
struct btrfs_key other_key;
|
2008-10-09 23:46:19 +08:00
|
|
|
struct btrfs_leaf_ref *ref;
|
|
|
|
u64 leaf_gen;
|
|
|
|
u64 leaf_start;
|
2008-10-02 07:05:46 +08:00
|
|
|
|
|
|
|
path->lowest_level = 1;
|
|
|
|
key.objectid = inode->i_ino;
|
|
|
|
key.type = BTRFS_CSUM_ITEM_KEY;
|
|
|
|
key.offset = new_size;
|
|
|
|
again:
|
|
|
|
ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (path->nodes[1] == NULL) {
|
|
|
|
ret = 0;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
btrfs_node_key_to_cpu(path->nodes[1], &found_key, path->slots[1]);
|
|
|
|
nritems = btrfs_header_nritems(path->nodes[1]);
|
|
|
|
|
|
|
|
if (!nritems)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (path->slots[1] >= nritems)
|
|
|
|
goto next_node;
|
|
|
|
|
|
|
|
/* did we find a key greater than anything we want to delete? */
|
|
|
|
if (found_key.objectid > inode->i_ino ||
|
|
|
|
(found_key.objectid == inode->i_ino && found_key.type > key.type))
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
/* we check the next key in the node to make sure the leave contains
|
|
|
|
* only checksum items. This comparison doesn't work if our
|
|
|
|
* leaf is the last one in the node
|
|
|
|
*/
|
|
|
|
if (path->slots[1] + 1 >= nritems) {
|
|
|
|
next_node:
|
|
|
|
/* search forward from the last key in the node, this
|
|
|
|
* will bring us into the next node in the tree
|
|
|
|
*/
|
|
|
|
btrfs_node_key_to_cpu(path->nodes[1], &found_key, nritems - 1);
|
|
|
|
|
|
|
|
/* unlikely, but we inc below, so check to be safe */
|
|
|
|
if (found_key.offset == (u64)-1)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
/* search_forward needs a path with locks held, do the
|
|
|
|
* search again for the original key. It is possible
|
|
|
|
* this will race with a balance and return a path that
|
|
|
|
* we could modify, but this drop is just an optimization
|
|
|
|
* and is allowed to miss some leaves.
|
|
|
|
*/
|
|
|
|
btrfs_release_path(root, path);
|
|
|
|
found_key.offset++;
|
|
|
|
|
|
|
|
/* setup a max key for search_forward */
|
|
|
|
other_key.offset = (u64)-1;
|
|
|
|
other_key.type = key.type;
|
|
|
|
other_key.objectid = key.objectid;
|
|
|
|
|
|
|
|
path->keep_locks = 1;
|
|
|
|
ret = btrfs_search_forward(root, &found_key, &other_key,
|
|
|
|
path, 0, 0);
|
|
|
|
path->keep_locks = 0;
|
|
|
|
if (ret || found_key.objectid != key.objectid ||
|
|
|
|
found_key.type != key.type) {
|
|
|
|
ret = 0;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
key.offset = found_key.offset;
|
|
|
|
btrfs_release_path(root, path);
|
|
|
|
cond_resched();
|
|
|
|
goto again;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* we know there's one more slot after us in the tree,
|
|
|
|
* read that key so we can verify it is also a checksum item
|
|
|
|
*/
|
|
|
|
btrfs_node_key_to_cpu(path->nodes[1], &other_key, path->slots[1] + 1);
|
|
|
|
|
|
|
|
if (found_key.objectid < inode->i_ino)
|
|
|
|
goto next_key;
|
|
|
|
|
|
|
|
if (found_key.type != key.type || found_key.offset < new_size)
|
|
|
|
goto next_key;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* if the key for the next leaf isn't a csum key from this objectid,
|
|
|
|
* we can't be sure there aren't good items inside this leaf.
|
|
|
|
* Bail out
|
|
|
|
*/
|
|
|
|
if (other_key.objectid != inode->i_ino || other_key.type != key.type)
|
|
|
|
goto out;
|
|
|
|
|
2008-10-09 23:46:19 +08:00
|
|
|
leaf_start = btrfs_node_blockptr(path->nodes[1], path->slots[1]);
|
|
|
|
leaf_gen = btrfs_node_ptr_generation(path->nodes[1], path->slots[1]);
|
2008-10-02 07:05:46 +08:00
|
|
|
/*
|
|
|
|
* it is safe to delete this leaf, it contains only
|
|
|
|
* csum items from this inode at an offset >= new_size
|
|
|
|
*/
|
2008-10-09 23:46:19 +08:00
|
|
|
ret = btrfs_del_leaf(trans, root, path, leaf_start);
|
2008-10-02 07:05:46 +08:00
|
|
|
BUG_ON(ret);
|
|
|
|
|
2008-10-09 23:46:19 +08:00
|
|
|
if (root->ref_cows && leaf_gen < trans->transid) {
|
|
|
|
ref = btrfs_alloc_leaf_ref(root, 0);
|
|
|
|
if (ref) {
|
|
|
|
ref->root_gen = root->root_key.offset;
|
|
|
|
ref->bytenr = leaf_start;
|
|
|
|
ref->owner = 0;
|
|
|
|
ref->generation = leaf_gen;
|
|
|
|
ref->nritems = 0;
|
|
|
|
|
2009-02-04 22:27:02 +08:00
|
|
|
btrfs_sort_leaf_ref(ref);
|
|
|
|
|
2008-10-09 23:46:19 +08:00
|
|
|
ret = btrfs_add_leaf_ref(root, ref, 0);
|
|
|
|
WARN_ON(ret);
|
|
|
|
btrfs_free_leaf_ref(root, ref);
|
|
|
|
} else {
|
|
|
|
WARN_ON(1);
|
|
|
|
}
|
|
|
|
}
|
2008-10-02 07:05:46 +08:00
|
|
|
next_key:
|
|
|
|
btrfs_release_path(root, path);
|
|
|
|
|
|
|
|
if (other_key.objectid == inode->i_ino &&
|
|
|
|
other_key.type == key.type && other_key.offset > key.offset) {
|
|
|
|
key.offset = other_key.offset;
|
|
|
|
cond_resched();
|
|
|
|
goto again;
|
|
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
out:
|
|
|
|
/* fixup any changes we've made to the path */
|
|
|
|
path->lowest_level = 0;
|
|
|
|
path->keep_locks = 0;
|
|
|
|
btrfs_release_path(root, path);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
Btrfs: move data checksumming into a dedicated tree
Btrfs stores checksums for each data block. Until now, they have
been stored in the subvolume trees, indexed by the inode that is
referencing the data block. This means that when we read the inode,
we've probably read in at least some checksums as well.
But, this has a few problems:
* The checksums are indexed by logical offset in the file. When
compression is on, this means we have to do the expensive checksumming
on the uncompressed data. It would be faster if we could checksum
the compressed data instead.
* If we implement encryption, we'll be checksumming the plain text and
storing that on disk. This is significantly less secure.
* For either compression or encryption, we have to get the plain text
back before we can verify the checksum as correct. This makes the raid
layer balancing and extent moving much more expensive.
* It makes the front end caching code more complex, as we have touch
the subvolume and inodes as we cache extents.
* There is potentitally one copy of the checksum in each subvolume
referencing an extent.
The solution used here is to store the extent checksums in a dedicated
tree. This allows us to index the checksums by phyiscal extent
start and length. It means:
* The checksum is against the data stored on disk, after any compression
or encryption is done.
* The checksum is stored in a central location, and can be verified without
following back references, or reading inodes.
This makes compression significantly faster by reducing the amount of
data that needs to be checksummed. It will also allow much faster
raid management code in general.
The checksums are indexed by a key with a fixed objectid (a magic value
in ctree.h) and offset set to the starting byte of the extent. This
allows us to copy the checksum items into the fsync log tree directly (or
any other tree), without having to invent a second format for them.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-12-09 05:58:54 +08:00
|
|
|
#endif
|
|
|
|
|
2007-06-12 18:35:45 +08:00
|
|
|
/*
|
|
|
|
* this can truncate away extent items, csum items and directory items.
|
|
|
|
* It starts at a high offset and removes keys until it can't find
|
2008-09-30 03:18:18 +08:00
|
|
|
* any higher than new_size
|
2007-06-12 18:35:45 +08:00
|
|
|
*
|
|
|
|
* csum items that cross the new i_size are truncated to the new size
|
|
|
|
* as well.
|
2008-07-25 00:17:14 +08:00
|
|
|
*
|
|
|
|
* min_type is the minimum key type to truncate down to. If set to 0, this
|
|
|
|
* will kill all the items on this inode, including the INODE_ITEM_KEY.
|
2007-06-12 18:35:45 +08:00
|
|
|
*/
|
2008-09-06 04:13:11 +08:00
|
|
|
noinline int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans,
|
|
|
|
struct btrfs_root *root,
|
|
|
|
struct inode *inode,
|
|
|
|
u64 new_size, u32 min_type)
|
2007-06-12 18:35:45 +08:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
struct btrfs_path *path;
|
|
|
|
struct btrfs_key key;
|
2007-10-16 04:14:19 +08:00
|
|
|
struct btrfs_key found_key;
|
2009-02-04 22:30:58 +08:00
|
|
|
u32 found_type = (u8)-1;
|
2007-10-16 04:14:19 +08:00
|
|
|
struct extent_buffer *leaf;
|
2007-06-12 18:35:45 +08:00
|
|
|
struct btrfs_file_extent_item *fi;
|
|
|
|
u64 extent_start = 0;
|
2007-10-16 04:15:53 +08:00
|
|
|
u64 extent_num_bytes = 0;
|
Btrfs: Mixed back reference (FORWARD ROLLING FORMAT CHANGE)
This commit introduces a new kind of back reference for btrfs metadata.
Once a filesystem has been mounted with this commit, IT WILL NO LONGER
BE MOUNTABLE BY OLDER KERNELS.
When a tree block in subvolume tree is cow'd, the reference counts of all
extents it points to are increased by one. At transaction commit time,
the old root of the subvolume is recorded in a "dead root" data structure,
and the btree it points to is later walked, dropping reference counts
and freeing any blocks where the reference count goes to 0.
The increments done during cow and decrements done after commit cancel out,
and the walk is a very expensive way to go about freeing the blocks that
are no longer referenced by the new btree root. This commit reduces the
transaction overhead by avoiding the need for dead root records.
When a non-shared tree block is cow'd, we free the old block at once, and the
new block inherits old block's references. When a tree block with reference
count > 1 is cow'd, we increase the reference counts of all extents
the new block points to by one, and decrease the old block's reference count by
one.
This dead tree avoidance code removes the need to modify the reference
counts of lower level extents when a non-shared tree block is cow'd.
But we still need to update back ref for all pointers in the block.
This is because the location of the block is recorded in the back ref
item.
We can solve this by introducing a new type of back ref. The new
back ref provides information about pointer's key, level and in which
tree the pointer lives. This information allow us to find the pointer
by searching the tree. The shortcoming of the new back ref is that it
only works for pointers in tree blocks referenced by their owner trees.
This is mostly a problem for snapshots, where resolving one of these
fuzzy back references would be O(number_of_snapshots) and quite slow.
The solution used here is to use the fuzzy back references in the common
case where a given tree block is only referenced by one root,
and use the full back references when multiple roots have a reference
on a given block.
This commit adds per subvolume red-black tree to keep trace of cached
inodes. The red-black tree helps the balancing code to find cached
inodes whose inode numbers within a given range.
This commit improves the balancing code by introducing several data
structures to keep the state of balancing. The most important one
is the back ref cache. It caches how the upper level tree blocks are
referenced. This greatly reduce the overhead of checking back ref.
The improved balancing code scales significantly better with a large
number of snapshots.
This is a very large commit and was written in a number of
pieces. But, they depend heavily on the disk format change and were
squashed together to make sure git bisect didn't end up in a
bad state wrt space balancing or the format change.
Signed-off-by: Yan Zheng <zheng.yan@oracle.com>
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2009-06-10 22:45:14 +08:00
|
|
|
u64 extent_offset = 0;
|
2007-06-12 18:35:45 +08:00
|
|
|
u64 item_end = 0;
|
|
|
|
int found_extent;
|
|
|
|
int del_item;
|
2008-01-30 04:11:36 +08:00
|
|
|
int pending_del_nr = 0;
|
|
|
|
int pending_del_slot = 0;
|
2007-11-01 23:28:41 +08:00
|
|
|
int extent_type = -1;
|
2008-11-07 11:02:51 +08:00
|
|
|
int encoding;
|
2008-04-17 23:29:12 +08:00
|
|
|
u64 mask = root->sectorsize - 1;
|
2007-06-12 18:35:45 +08:00
|
|
|
|
2008-09-06 04:13:11 +08:00
|
|
|
if (root->ref_cows)
|
2008-09-26 22:05:38 +08:00
|
|
|
btrfs_drop_extent_cache(inode, new_size & (~mask), (u64)-1, 0);
|
2007-06-12 18:35:45 +08:00
|
|
|
path = btrfs_alloc_path();
|
|
|
|
BUG_ON(!path);
|
2009-07-23 04:49:01 +08:00
|
|
|
path->reada = -1;
|
2007-10-16 04:14:19 +08:00
|
|
|
|
2007-06-12 18:35:45 +08:00
|
|
|
/* FIXME, add redo link to tree so we don't leak on crash */
|
|
|
|
key.objectid = inode->i_ino;
|
|
|
|
key.offset = (u64)-1;
|
2007-10-16 04:14:19 +08:00
|
|
|
key.type = (u8)-1;
|
|
|
|
|
2008-01-30 04:11:36 +08:00
|
|
|
search_again:
|
2009-03-13 23:00:37 +08:00
|
|
|
path->leave_spinning = 1;
|
2008-01-30 04:11:36 +08:00
|
|
|
ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
|
2009-01-06 10:25:51 +08:00
|
|
|
if (ret < 0)
|
2008-01-30 04:11:36 +08:00
|
|
|
goto error;
|
2009-01-06 10:25:51 +08:00
|
|
|
|
2008-01-30 04:11:36 +08:00
|
|
|
if (ret > 0) {
|
2008-09-06 04:13:11 +08:00
|
|
|
/* there are no items in the tree for us to truncate, we're
|
|
|
|
* done
|
|
|
|
*/
|
|
|
|
if (path->slots[0] == 0) {
|
|
|
|
ret = 0;
|
|
|
|
goto error;
|
|
|
|
}
|
2008-01-30 04:11:36 +08:00
|
|
|
path->slots[0]--;
|
|
|
|
}
|
|
|
|
|
2009-01-06 10:25:51 +08:00
|
|
|
while (1) {
|
2007-06-12 18:35:45 +08:00
|
|
|
fi = NULL;
|
2007-10-16 04:14:19 +08:00
|
|
|
leaf = path->nodes[0];
|
|
|
|
btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
|
|
|
|
found_type = btrfs_key_type(&found_key);
|
2008-11-07 11:02:51 +08:00
|
|
|
encoding = 0;
|
2007-06-12 18:35:45 +08:00
|
|
|
|
2007-10-16 04:14:19 +08:00
|
|
|
if (found_key.objectid != inode->i_ino)
|
2007-06-12 18:35:45 +08:00
|
|
|
break;
|
2007-10-16 04:14:19 +08:00
|
|
|
|
2008-01-30 04:11:36 +08:00
|
|
|
if (found_type < min_type)
|
2007-06-12 18:35:45 +08:00
|
|
|
break;
|
|
|
|
|
2007-10-16 04:14:19 +08:00
|
|
|
item_end = found_key.offset;
|
2007-06-12 18:35:45 +08:00
|
|
|
if (found_type == BTRFS_EXTENT_DATA_KEY) {
|
2007-10-16 04:14:19 +08:00
|
|
|
fi = btrfs_item_ptr(leaf, path->slots[0],
|
2007-06-12 18:35:45 +08:00
|
|
|
struct btrfs_file_extent_item);
|
2007-11-01 23:28:41 +08:00
|
|
|
extent_type = btrfs_file_extent_type(leaf, fi);
|
2008-11-07 11:02:51 +08:00
|
|
|
encoding = btrfs_file_extent_compression(leaf, fi);
|
|
|
|
encoding |= btrfs_file_extent_encryption(leaf, fi);
|
|
|
|
encoding |= btrfs_file_extent_other_encoding(leaf, fi);
|
|
|
|
|
2007-11-01 23:28:41 +08:00
|
|
|
if (extent_type != BTRFS_FILE_EXTENT_INLINE) {
|
2007-10-16 04:14:19 +08:00
|
|
|
item_end +=
|
2007-10-16 04:15:53 +08:00
|
|
|
btrfs_file_extent_num_bytes(leaf, fi);
|
2007-11-01 23:28:41 +08:00
|
|
|
} else if (extent_type == BTRFS_FILE_EXTENT_INLINE) {
|
|
|
|
item_end += btrfs_file_extent_inline_len(leaf,
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
fi);
|
2007-06-12 18:35:45 +08:00
|
|
|
}
|
2007-11-08 02:31:09 +08:00
|
|
|
item_end--;
|
2007-06-12 18:35:45 +08:00
|
|
|
}
|
2008-09-06 04:13:11 +08:00
|
|
|
if (item_end < new_size) {
|
2009-01-06 10:25:51 +08:00
|
|
|
if (found_type == BTRFS_DIR_ITEM_KEY)
|
2007-08-28 04:49:44 +08:00
|
|
|
found_type = BTRFS_INODE_ITEM_KEY;
|
2009-01-06 10:25:51 +08:00
|
|
|
else if (found_type == BTRFS_EXTENT_ITEM_KEY)
|
Btrfs: move data checksumming into a dedicated tree
Btrfs stores checksums for each data block. Until now, they have
been stored in the subvolume trees, indexed by the inode that is
referencing the data block. This means that when we read the inode,
we've probably read in at least some checksums as well.
But, this has a few problems:
* The checksums are indexed by logical offset in the file. When
compression is on, this means we have to do the expensive checksumming
on the uncompressed data. It would be faster if we could checksum
the compressed data instead.
* If we implement encryption, we'll be checksumming the plain text and
storing that on disk. This is significantly less secure.
* For either compression or encryption, we have to get the plain text
back before we can verify the checksum as correct. This makes the raid
layer balancing and extent moving much more expensive.
* It makes the front end caching code more complex, as we have touch
the subvolume and inodes as we cache extents.
* There is potentitally one copy of the checksum in each subvolume
referencing an extent.
The solution used here is to store the extent checksums in a dedicated
tree. This allows us to index the checksums by phyiscal extent
start and length. It means:
* The checksum is against the data stored on disk, after any compression
or encryption is done.
* The checksum is stored in a central location, and can be verified without
following back references, or reading inodes.
This makes compression significantly faster by reducing the amount of
data that needs to be checksummed. It will also allow much faster
raid management code in general.
The checksums are indexed by a key with a fixed objectid (a magic value
in ctree.h) and offset set to the starting byte of the extent. This
allows us to copy the checksum items into the fsync log tree directly (or
any other tree), without having to invent a second format for them.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-12-09 05:58:54 +08:00
|
|
|
found_type = BTRFS_EXTENT_DATA_KEY;
|
2009-01-06 10:25:51 +08:00
|
|
|
else if (found_type == BTRFS_EXTENT_DATA_KEY)
|
2008-01-30 04:11:36 +08:00
|
|
|
found_type = BTRFS_XATTR_ITEM_KEY;
|
2009-01-06 10:25:51 +08:00
|
|
|
else if (found_type == BTRFS_XATTR_ITEM_KEY)
|
2008-01-30 04:11:36 +08:00
|
|
|
found_type = BTRFS_INODE_REF_KEY;
|
2009-01-06 10:25:51 +08:00
|
|
|
else if (found_type)
|
2007-08-28 04:49:44 +08:00
|
|
|
found_type--;
|
2009-01-06 10:25:51 +08:00
|
|
|
else
|
2007-08-28 04:49:44 +08:00
|
|
|
break;
|
2007-09-17 23:08:38 +08:00
|
|
|
btrfs_set_key_type(&key, found_type);
|
2008-01-30 04:11:36 +08:00
|
|
|
goto next;
|
2007-06-12 18:35:45 +08:00
|
|
|
}
|
2008-09-06 04:13:11 +08:00
|
|
|
if (found_key.offset >= new_size)
|
2007-06-12 18:35:45 +08:00
|
|
|
del_item = 1;
|
|
|
|
else
|
|
|
|
del_item = 0;
|
|
|
|
found_extent = 0;
|
|
|
|
|
|
|
|
/* FIXME, shrink the extent if the ref count is only 1 */
|
2007-11-01 23:28:41 +08:00
|
|
|
if (found_type != BTRFS_EXTENT_DATA_KEY)
|
|
|
|
goto delete;
|
|
|
|
|
|
|
|
if (extent_type != BTRFS_FILE_EXTENT_INLINE) {
|
2007-06-12 18:35:45 +08:00
|
|
|
u64 num_dec;
|
2007-10-16 04:15:53 +08:00
|
|
|
extent_start = btrfs_file_extent_disk_bytenr(leaf, fi);
|
2008-11-07 11:02:51 +08:00
|
|
|
if (!del_item && !encoding) {
|
2007-10-16 04:15:53 +08:00
|
|
|
u64 orig_num_bytes =
|
|
|
|
btrfs_file_extent_num_bytes(leaf, fi);
|
2008-09-06 04:13:11 +08:00
|
|
|
extent_num_bytes = new_size -
|
2007-10-16 04:14:19 +08:00
|
|
|
found_key.offset + root->sectorsize - 1;
|
2008-01-31 00:54:04 +08:00
|
|
|
extent_num_bytes = extent_num_bytes &
|
|
|
|
~((u64)root->sectorsize - 1);
|
2007-10-16 04:15:53 +08:00
|
|
|
btrfs_set_file_extent_num_bytes(leaf, fi,
|
|
|
|
extent_num_bytes);
|
|
|
|
num_dec = (orig_num_bytes -
|
2008-02-09 02:49:28 +08:00
|
|
|
extent_num_bytes);
|
2008-09-06 04:13:11 +08:00
|
|
|
if (root->ref_cows && extent_start != 0)
|
2008-10-09 23:46:29 +08:00
|
|
|
inode_sub_bytes(inode, num_dec);
|
2007-10-16 04:14:19 +08:00
|
|
|
btrfs_mark_buffer_dirty(leaf);
|
2007-06-12 18:35:45 +08:00
|
|
|
} else {
|
2007-10-16 04:15:53 +08:00
|
|
|
extent_num_bytes =
|
|
|
|
btrfs_file_extent_disk_num_bytes(leaf,
|
|
|
|
fi);
|
Btrfs: Mixed back reference (FORWARD ROLLING FORMAT CHANGE)
This commit introduces a new kind of back reference for btrfs metadata.
Once a filesystem has been mounted with this commit, IT WILL NO LONGER
BE MOUNTABLE BY OLDER KERNELS.
When a tree block in subvolume tree is cow'd, the reference counts of all
extents it points to are increased by one. At transaction commit time,
the old root of the subvolume is recorded in a "dead root" data structure,
and the btree it points to is later walked, dropping reference counts
and freeing any blocks where the reference count goes to 0.
The increments done during cow and decrements done after commit cancel out,
and the walk is a very expensive way to go about freeing the blocks that
are no longer referenced by the new btree root. This commit reduces the
transaction overhead by avoiding the need for dead root records.
When a non-shared tree block is cow'd, we free the old block at once, and the
new block inherits old block's references. When a tree block with reference
count > 1 is cow'd, we increase the reference counts of all extents
the new block points to by one, and decrease the old block's reference count by
one.
This dead tree avoidance code removes the need to modify the reference
counts of lower level extents when a non-shared tree block is cow'd.
But we still need to update back ref for all pointers in the block.
This is because the location of the block is recorded in the back ref
item.
We can solve this by introducing a new type of back ref. The new
back ref provides information about pointer's key, level and in which
tree the pointer lives. This information allow us to find the pointer
by searching the tree. The shortcoming of the new back ref is that it
only works for pointers in tree blocks referenced by their owner trees.
This is mostly a problem for snapshots, where resolving one of these
fuzzy back references would be O(number_of_snapshots) and quite slow.
The solution used here is to use the fuzzy back references in the common
case where a given tree block is only referenced by one root,
and use the full back references when multiple roots have a reference
on a given block.
This commit adds per subvolume red-black tree to keep trace of cached
inodes. The red-black tree helps the balancing code to find cached
inodes whose inode numbers within a given range.
This commit improves the balancing code by introducing several data
structures to keep the state of balancing. The most important one
is the back ref cache. It caches how the upper level tree blocks are
referenced. This greatly reduce the overhead of checking back ref.
The improved balancing code scales significantly better with a large
number of snapshots.
This is a very large commit and was written in a number of
pieces. But, they depend heavily on the disk format change and were
squashed together to make sure git bisect didn't end up in a
bad state wrt space balancing or the format change.
Signed-off-by: Yan Zheng <zheng.yan@oracle.com>
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2009-06-10 22:45:14 +08:00
|
|
|
extent_offset = found_key.offset -
|
|
|
|
btrfs_file_extent_offset(leaf, fi);
|
|
|
|
|
2007-06-12 18:35:45 +08:00
|
|
|
/* FIXME blocksize != 4096 */
|
2008-02-09 02:49:28 +08:00
|
|
|
num_dec = btrfs_file_extent_num_bytes(leaf, fi);
|
2007-06-12 18:35:45 +08:00
|
|
|
if (extent_start != 0) {
|
|
|
|
found_extent = 1;
|
2008-09-06 04:13:11 +08:00
|
|
|
if (root->ref_cows)
|
2008-10-09 23:46:29 +08:00
|
|
|
inode_sub_bytes(inode, num_dec);
|
2008-09-06 04:13:11 +08:00
|
|
|
}
|
2007-06-12 18:35:45 +08:00
|
|
|
}
|
2008-02-09 02:49:28 +08:00
|
|
|
} else if (extent_type == BTRFS_FILE_EXTENT_INLINE) {
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
/*
|
|
|
|
* we can't truncate inline items that have had
|
|
|
|
* special encodings
|
|
|
|
*/
|
|
|
|
if (!del_item &&
|
|
|
|
btrfs_file_extent_compression(leaf, fi) == 0 &&
|
|
|
|
btrfs_file_extent_encryption(leaf, fi) == 0 &&
|
|
|
|
btrfs_file_extent_other_encoding(leaf, fi) == 0) {
|
2008-09-06 04:13:11 +08:00
|
|
|
u32 size = new_size - found_key.offset;
|
|
|
|
|
|
|
|
if (root->ref_cows) {
|
2008-10-09 23:46:29 +08:00
|
|
|
inode_sub_bytes(inode, item_end + 1 -
|
|
|
|
new_size);
|
2008-09-06 04:13:11 +08:00
|
|
|
}
|
|
|
|
size =
|
|
|
|
btrfs_file_extent_calc_inline_size(size);
|
2008-02-09 02:49:28 +08:00
|
|
|
ret = btrfs_truncate_item(trans, root, path,
|
2008-09-06 04:13:11 +08:00
|
|
|
size, 1);
|
2008-02-09 02:49:28 +08:00
|
|
|
BUG_ON(ret);
|
2008-09-06 04:13:11 +08:00
|
|
|
} else if (root->ref_cows) {
|
2008-10-09 23:46:29 +08:00
|
|
|
inode_sub_bytes(inode, item_end + 1 -
|
|
|
|
found_key.offset);
|
2008-02-09 02:49:28 +08:00
|
|
|
}
|
2007-06-12 18:35:45 +08:00
|
|
|
}
|
2007-11-01 23:28:41 +08:00
|
|
|
delete:
|
2007-06-12 18:35:45 +08:00
|
|
|
if (del_item) {
|
2008-01-30 04:11:36 +08:00
|
|
|
if (!pending_del_nr) {
|
|
|
|
/* no pending yet, add ourselves */
|
|
|
|
pending_del_slot = path->slots[0];
|
|
|
|
pending_del_nr = 1;
|
|
|
|
} else if (pending_del_nr &&
|
|
|
|
path->slots[0] + 1 == pending_del_slot) {
|
|
|
|
/* hop on the pending chunk */
|
|
|
|
pending_del_nr++;
|
|
|
|
pending_del_slot = path->slots[0];
|
|
|
|
} else {
|
2009-01-06 10:25:51 +08:00
|
|
|
BUG();
|
2008-01-30 04:11:36 +08:00
|
|
|
}
|
2007-06-12 18:35:45 +08:00
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
Btrfs: Mixed back reference (FORWARD ROLLING FORMAT CHANGE)
This commit introduces a new kind of back reference for btrfs metadata.
Once a filesystem has been mounted with this commit, IT WILL NO LONGER
BE MOUNTABLE BY OLDER KERNELS.
When a tree block in subvolume tree is cow'd, the reference counts of all
extents it points to are increased by one. At transaction commit time,
the old root of the subvolume is recorded in a "dead root" data structure,
and the btree it points to is later walked, dropping reference counts
and freeing any blocks where the reference count goes to 0.
The increments done during cow and decrements done after commit cancel out,
and the walk is a very expensive way to go about freeing the blocks that
are no longer referenced by the new btree root. This commit reduces the
transaction overhead by avoiding the need for dead root records.
When a non-shared tree block is cow'd, we free the old block at once, and the
new block inherits old block's references. When a tree block with reference
count > 1 is cow'd, we increase the reference counts of all extents
the new block points to by one, and decrease the old block's reference count by
one.
This dead tree avoidance code removes the need to modify the reference
counts of lower level extents when a non-shared tree block is cow'd.
But we still need to update back ref for all pointers in the block.
This is because the location of the block is recorded in the back ref
item.
We can solve this by introducing a new type of back ref. The new
back ref provides information about pointer's key, level and in which
tree the pointer lives. This information allow us to find the pointer
by searching the tree. The shortcoming of the new back ref is that it
only works for pointers in tree blocks referenced by their owner trees.
This is mostly a problem for snapshots, where resolving one of these
fuzzy back references would be O(number_of_snapshots) and quite slow.
The solution used here is to use the fuzzy back references in the common
case where a given tree block is only referenced by one root,
and use the full back references when multiple roots have a reference
on a given block.
This commit adds per subvolume red-black tree to keep trace of cached
inodes. The red-black tree helps the balancing code to find cached
inodes whose inode numbers within a given range.
This commit improves the balancing code by introducing several data
structures to keep the state of balancing. The most important one
is the back ref cache. It caches how the upper level tree blocks are
referenced. This greatly reduce the overhead of checking back ref.
The improved balancing code scales significantly better with a large
number of snapshots.
This is a very large commit and was written in a number of
pieces. But, they depend heavily on the disk format change and were
squashed together to make sure git bisect didn't end up in a
bad state wrt space balancing or the format change.
Signed-off-by: Yan Zheng <zheng.yan@oracle.com>
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2009-06-10 22:45:14 +08:00
|
|
|
if (found_extent && root->ref_cows) {
|
2009-03-13 23:00:37 +08:00
|
|
|
btrfs_set_path_blocking(path);
|
2007-06-12 18:35:45 +08:00
|
|
|
ret = btrfs_free_extent(trans, root, extent_start,
|
Btrfs: Mixed back reference (FORWARD ROLLING FORMAT CHANGE)
This commit introduces a new kind of back reference for btrfs metadata.
Once a filesystem has been mounted with this commit, IT WILL NO LONGER
BE MOUNTABLE BY OLDER KERNELS.
When a tree block in subvolume tree is cow'd, the reference counts of all
extents it points to are increased by one. At transaction commit time,
the old root of the subvolume is recorded in a "dead root" data structure,
and the btree it points to is later walked, dropping reference counts
and freeing any blocks where the reference count goes to 0.
The increments done during cow and decrements done after commit cancel out,
and the walk is a very expensive way to go about freeing the blocks that
are no longer referenced by the new btree root. This commit reduces the
transaction overhead by avoiding the need for dead root records.
When a non-shared tree block is cow'd, we free the old block at once, and the
new block inherits old block's references. When a tree block with reference
count > 1 is cow'd, we increase the reference counts of all extents
the new block points to by one, and decrease the old block's reference count by
one.
This dead tree avoidance code removes the need to modify the reference
counts of lower level extents when a non-shared tree block is cow'd.
But we still need to update back ref for all pointers in the block.
This is because the location of the block is recorded in the back ref
item.
We can solve this by introducing a new type of back ref. The new
back ref provides information about pointer's key, level and in which
tree the pointer lives. This information allow us to find the pointer
by searching the tree. The shortcoming of the new back ref is that it
only works for pointers in tree blocks referenced by their owner trees.
This is mostly a problem for snapshots, where resolving one of these
fuzzy back references would be O(number_of_snapshots) and quite slow.
The solution used here is to use the fuzzy back references in the common
case where a given tree block is only referenced by one root,
and use the full back references when multiple roots have a reference
on a given block.
This commit adds per subvolume red-black tree to keep trace of cached
inodes. The red-black tree helps the balancing code to find cached
inodes whose inode numbers within a given range.
This commit improves the balancing code by introducing several data
structures to keep the state of balancing. The most important one
is the back ref cache. It caches how the upper level tree blocks are
referenced. This greatly reduce the overhead of checking back ref.
The improved balancing code scales significantly better with a large
number of snapshots.
This is a very large commit and was written in a number of
pieces. But, they depend heavily on the disk format change and were
squashed together to make sure git bisect didn't end up in a
bad state wrt space balancing or the format change.
Signed-off-by: Yan Zheng <zheng.yan@oracle.com>
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2009-06-10 22:45:14 +08:00
|
|
|
extent_num_bytes, 0,
|
|
|
|
btrfs_header_owner(leaf),
|
|
|
|
inode->i_ino, extent_offset);
|
2007-06-12 18:35:45 +08:00
|
|
|
BUG_ON(ret);
|
|
|
|
}
|
2008-01-30 04:11:36 +08:00
|
|
|
next:
|
|
|
|
if (path->slots[0] == 0) {
|
|
|
|
if (pending_del_nr)
|
|
|
|
goto del_pending;
|
|
|
|
btrfs_release_path(root, path);
|
2009-02-04 22:30:58 +08:00
|
|
|
if (found_type == BTRFS_INODE_ITEM_KEY)
|
|
|
|
break;
|
2008-01-30 04:11:36 +08:00
|
|
|
goto search_again;
|
|
|
|
}
|
|
|
|
|
|
|
|
path->slots[0]--;
|
|
|
|
if (pending_del_nr &&
|
|
|
|
path->slots[0] + 1 != pending_del_slot) {
|
|
|
|
struct btrfs_key debug;
|
|
|
|
del_pending:
|
|
|
|
btrfs_item_key_to_cpu(path->nodes[0], &debug,
|
|
|
|
pending_del_slot);
|
|
|
|
ret = btrfs_del_items(trans, root, path,
|
|
|
|
pending_del_slot,
|
|
|
|
pending_del_nr);
|
|
|
|
BUG_ON(ret);
|
|
|
|
pending_del_nr = 0;
|
|
|
|
btrfs_release_path(root, path);
|
2009-02-04 22:30:58 +08:00
|
|
|
if (found_type == BTRFS_INODE_ITEM_KEY)
|
|
|
|
break;
|
2008-01-30 04:11:36 +08:00
|
|
|
goto search_again;
|
|
|
|
}
|
2007-06-12 18:35:45 +08:00
|
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
error:
|
2008-01-30 04:11:36 +08:00
|
|
|
if (pending_del_nr) {
|
|
|
|
ret = btrfs_del_items(trans, root, path, pending_del_slot,
|
|
|
|
pending_del_nr);
|
|
|
|
}
|
2007-06-12 18:35:45 +08:00
|
|
|
btrfs_free_path(path);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* taken from block_truncate_page, but does cow as it zeros out
|
|
|
|
* any bytes left in the last page in the file.
|
|
|
|
*/
|
|
|
|
static int btrfs_truncate_page(struct address_space *mapping, loff_t from)
|
|
|
|
{
|
|
|
|
struct inode *inode = mapping->host;
|
2007-10-16 04:15:53 +08:00
|
|
|
struct btrfs_root *root = BTRFS_I(inode)->root;
|
2008-07-18 00:53:50 +08:00
|
|
|
struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree;
|
|
|
|
struct btrfs_ordered_extent *ordered;
|
|
|
|
char *kaddr;
|
2007-10-16 04:15:53 +08:00
|
|
|
u32 blocksize = root->sectorsize;
|
2007-06-12 18:35:45 +08:00
|
|
|
pgoff_t index = from >> PAGE_CACHE_SHIFT;
|
|
|
|
unsigned offset = from & (PAGE_CACHE_SIZE-1);
|
|
|
|
struct page *page;
|
|
|
|
int ret = 0;
|
2007-08-28 04:49:44 +08:00
|
|
|
u64 page_start;
|
2008-07-18 00:53:50 +08:00
|
|
|
u64 page_end;
|
2007-06-12 18:35:45 +08:00
|
|
|
|
|
|
|
if ((offset & (blocksize - 1)) == 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
ret = -ENOMEM;
|
2008-05-15 21:13:45 +08:00
|
|
|
again:
|
2007-06-12 18:35:45 +08:00
|
|
|
page = grab_cache_page(mapping, index);
|
|
|
|
if (!page)
|
|
|
|
goto out;
|
2008-07-18 00:53:50 +08:00
|
|
|
|
|
|
|
page_start = page_offset(page);
|
|
|
|
page_end = page_start + PAGE_CACHE_SIZE - 1;
|
|
|
|
|
2007-06-12 18:35:45 +08:00
|
|
|
if (!PageUptodate(page)) {
|
2007-06-16 01:50:00 +08:00
|
|
|
ret = btrfs_readpage(NULL, page);
|
2007-06-12 18:35:45 +08:00
|
|
|
lock_page(page);
|
2008-05-15 21:13:45 +08:00
|
|
|
if (page->mapping != mapping) {
|
|
|
|
unlock_page(page);
|
|
|
|
page_cache_release(page);
|
|
|
|
goto again;
|
|
|
|
}
|
2007-06-12 18:35:45 +08:00
|
|
|
if (!PageUptodate(page)) {
|
|
|
|
ret = -EIO;
|
2008-07-24 21:41:53 +08:00
|
|
|
goto out_unlock;
|
2007-06-12 18:35:45 +08:00
|
|
|
}
|
|
|
|
}
|
2008-05-15 21:13:45 +08:00
|
|
|
wait_on_page_writeback(page);
|
2008-07-18 00:53:50 +08:00
|
|
|
|
|
|
|
lock_extent(io_tree, page_start, page_end, GFP_NOFS);
|
|
|
|
set_page_extent_mapped(page);
|
|
|
|
|
|
|
|
ordered = btrfs_lookup_ordered_extent(inode, page_start);
|
|
|
|
if (ordered) {
|
|
|
|
unlock_extent(io_tree, page_start, page_end, GFP_NOFS);
|
|
|
|
unlock_page(page);
|
|
|
|
page_cache_release(page);
|
2008-07-18 01:53:27 +08:00
|
|
|
btrfs_start_ordered_extent(inode, ordered, 1);
|
2008-07-18 00:53:50 +08:00
|
|
|
btrfs_put_ordered_extent(ordered);
|
|
|
|
goto again;
|
|
|
|
}
|
|
|
|
|
2009-09-12 04:12:44 +08:00
|
|
|
ret = btrfs_set_extent_delalloc(inode, page_start, page_end);
|
|
|
|
if (ret) {
|
|
|
|
unlock_extent(io_tree, page_start, page_end, GFP_NOFS);
|
|
|
|
goto out_unlock;
|
|
|
|
}
|
|
|
|
|
2008-07-18 00:53:50 +08:00
|
|
|
ret = 0;
|
|
|
|
if (offset != PAGE_CACHE_SIZE) {
|
|
|
|
kaddr = kmap(page);
|
|
|
|
memset(kaddr + offset, 0, PAGE_CACHE_SIZE - offset);
|
|
|
|
flush_dcache_page(page);
|
|
|
|
kunmap(page);
|
|
|
|
}
|
2008-07-18 00:53:51 +08:00
|
|
|
ClearPageChecked(page);
|
2008-07-18 00:53:50 +08:00
|
|
|
set_page_dirty(page);
|
|
|
|
unlock_extent(io_tree, page_start, page_end, GFP_NOFS);
|
2007-06-12 18:35:45 +08:00
|
|
|
|
2008-07-24 21:41:53 +08:00
|
|
|
out_unlock:
|
2007-06-12 18:35:45 +08:00
|
|
|
unlock_page(page);
|
|
|
|
page_cache_release(page);
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2008-10-31 02:19:41 +08:00
|
|
|
int btrfs_cont_expand(struct inode *inode, loff_t size)
|
2007-06-12 18:35:45 +08:00
|
|
|
{
|
2008-10-31 02:19:41 +08:00
|
|
|
struct btrfs_trans_handle *trans;
|
|
|
|
struct btrfs_root *root = BTRFS_I(inode)->root;
|
|
|
|
struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree;
|
|
|
|
struct extent_map *em;
|
|
|
|
u64 mask = root->sectorsize - 1;
|
|
|
|
u64 hole_start = (inode->i_size + mask) & ~mask;
|
|
|
|
u64 block_end = (size + mask) & ~mask;
|
|
|
|
u64 last_byte;
|
|
|
|
u64 cur_offset;
|
|
|
|
u64 hole_size;
|
2009-09-12 04:12:44 +08:00
|
|
|
int err = 0;
|
2007-06-12 18:35:45 +08:00
|
|
|
|
2008-10-31 02:19:41 +08:00
|
|
|
if (size <= hole_start)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
btrfs_truncate_page(inode->i_mapping, inode->i_size);
|
2007-08-30 23:54:02 +08:00
|
|
|
|
2008-10-31 02:19:41 +08:00
|
|
|
while (1) {
|
|
|
|
struct btrfs_ordered_extent *ordered;
|
|
|
|
btrfs_wait_ordered_range(inode, hole_start,
|
|
|
|
block_end - hole_start);
|
|
|
|
lock_extent(io_tree, hole_start, block_end - 1, GFP_NOFS);
|
|
|
|
ordered = btrfs_lookup_ordered_extent(inode, hole_start);
|
|
|
|
if (!ordered)
|
|
|
|
break;
|
|
|
|
unlock_extent(io_tree, hole_start, block_end - 1, GFP_NOFS);
|
|
|
|
btrfs_put_ordered_extent(ordered);
|
|
|
|
}
|
2007-06-12 18:35:45 +08:00
|
|
|
|
2008-10-31 02:19:41 +08:00
|
|
|
trans = btrfs_start_transaction(root, 1);
|
|
|
|
btrfs_set_trans_block_group(trans, inode);
|
2007-06-12 18:35:45 +08:00
|
|
|
|
2008-10-31 02:19:41 +08:00
|
|
|
cur_offset = hole_start;
|
|
|
|
while (1) {
|
|
|
|
em = btrfs_get_extent(inode, NULL, 0, cur_offset,
|
|
|
|
block_end - cur_offset, 0);
|
|
|
|
BUG_ON(IS_ERR(em) || !em);
|
|
|
|
last_byte = min(extent_map_end(em), block_end);
|
|
|
|
last_byte = (last_byte + mask) & ~mask;
|
|
|
|
if (test_bit(EXTENT_FLAG_VACANCY, &em->flags)) {
|
2008-11-07 11:02:51 +08:00
|
|
|
u64 hint_byte = 0;
|
2008-10-31 02:19:41 +08:00
|
|
|
hole_size = last_byte - cur_offset;
|
2008-11-07 11:02:51 +08:00
|
|
|
err = btrfs_drop_extents(trans, root, inode,
|
|
|
|
cur_offset,
|
|
|
|
cur_offset + hole_size,
|
2009-04-25 02:39:24 +08:00
|
|
|
block_end,
|
2009-09-12 00:27:37 +08:00
|
|
|
cur_offset, &hint_byte, 1);
|
2008-11-07 11:02:51 +08:00
|
|
|
if (err)
|
|
|
|
break;
|
2009-09-12 04:12:44 +08:00
|
|
|
|
|
|
|
err = btrfs_reserve_metadata_space(root, 1);
|
|
|
|
if (err)
|
|
|
|
break;
|
|
|
|
|
2008-10-31 02:19:41 +08:00
|
|
|
err = btrfs_insert_file_extent(trans, root,
|
|
|
|
inode->i_ino, cur_offset, 0,
|
|
|
|
0, hole_size, 0, hole_size,
|
|
|
|
0, 0, 0);
|
|
|
|
btrfs_drop_extent_cache(inode, hole_start,
|
|
|
|
last_byte - 1, 0);
|
2009-09-12 04:12:44 +08:00
|
|
|
btrfs_unreserve_metadata_space(root, 1);
|
2008-10-31 02:19:41 +08:00
|
|
|
}
|
|
|
|
free_extent_map(em);
|
|
|
|
cur_offset = last_byte;
|
|
|
|
if (err || cur_offset >= block_end)
|
|
|
|
break;
|
|
|
|
}
|
2007-12-22 05:27:21 +08:00
|
|
|
|
2008-10-31 02:19:41 +08:00
|
|
|
btrfs_end_transaction(trans, root);
|
|
|
|
unlock_extent(io_tree, hole_start, block_end - 1, GFP_NOFS);
|
|
|
|
return err;
|
|
|
|
}
|
2007-06-12 18:35:45 +08:00
|
|
|
|
2008-10-31 02:19:41 +08:00
|
|
|
static int btrfs_setattr(struct dentry *dentry, struct iattr *attr)
|
|
|
|
{
|
|
|
|
struct inode *inode = dentry->d_inode;
|
|
|
|
int err;
|
2007-06-12 18:35:45 +08:00
|
|
|
|
2008-10-31 02:19:41 +08:00
|
|
|
err = inode_change_ok(inode, attr);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2007-08-30 23:54:02 +08:00
|
|
|
|
2009-04-01 01:27:11 +08:00
|
|
|
if (S_ISREG(inode->i_mode) && (attr->ia_valid & ATTR_SIZE)) {
|
|
|
|
if (attr->ia_size > inode->i_size) {
|
|
|
|
err = btrfs_cont_expand(inode, attr->ia_size);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
} else if (inode->i_size > 0 &&
|
|
|
|
attr->ia_size == 0) {
|
|
|
|
|
|
|
|
/* we're truncating a file that used to have good
|
|
|
|
* data down to zero. Make sure it gets into
|
|
|
|
* the ordered flush list so that any new writes
|
|
|
|
* get down to disk quickly.
|
|
|
|
*/
|
|
|
|
BTRFS_I(inode)->ordered_data_close = 1;
|
|
|
|
}
|
2007-06-12 18:35:45 +08:00
|
|
|
}
|
2008-10-31 02:19:41 +08:00
|
|
|
|
2007-06-12 18:35:45 +08:00
|
|
|
err = inode_setattr(inode, attr);
|
2008-07-25 00:16:36 +08:00
|
|
|
|
|
|
|
if (!err && ((attr->ia_valid & ATTR_MODE)))
|
|
|
|
err = btrfs_acl_chmod(inode);
|
2007-06-12 18:35:45 +08:00
|
|
|
return err;
|
|
|
|
}
|
2008-01-15 05:24:38 +08:00
|
|
|
|
2007-06-12 18:35:45 +08:00
|
|
|
void btrfs_delete_inode(struct inode *inode)
|
|
|
|
{
|
|
|
|
struct btrfs_trans_handle *trans;
|
|
|
|
struct btrfs_root *root = BTRFS_I(inode)->root;
|
2007-09-17 22:58:06 +08:00
|
|
|
unsigned long nr;
|
2007-06-12 18:35:45 +08:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
truncate_inode_pages(&inode->i_data, 0);
|
|
|
|
if (is_bad_inode(inode)) {
|
2008-07-25 00:17:14 +08:00
|
|
|
btrfs_orphan_del(NULL, inode);
|
2007-06-12 18:35:45 +08:00
|
|
|
goto no_delete;
|
|
|
|
}
|
2008-07-21 22:29:44 +08:00
|
|
|
btrfs_wait_ordered_range(inode, 0, (u64)-1);
|
2007-10-16 04:14:19 +08:00
|
|
|
|
2009-09-22 04:00:26 +08:00
|
|
|
if (inode->i_nlink > 0) {
|
|
|
|
BUG_ON(btrfs_root_refs(&root->root_item) != 0);
|
|
|
|
goto no_delete;
|
|
|
|
}
|
|
|
|
|
2008-07-18 00:54:05 +08:00
|
|
|
btrfs_i_size_write(inode, 0);
|
2009-01-06 22:58:06 +08:00
|
|
|
trans = btrfs_join_transaction(root, 1);
|
2007-10-16 04:14:19 +08:00
|
|
|
|
2007-06-12 18:35:45 +08:00
|
|
|
btrfs_set_trans_block_group(trans, inode);
|
2008-09-06 04:13:11 +08:00
|
|
|
ret = btrfs_truncate_inode_items(trans, root, inode, inode->i_size, 0);
|
2008-07-25 00:17:14 +08:00
|
|
|
if (ret) {
|
|
|
|
btrfs_orphan_del(NULL, inode);
|
2007-06-23 02:16:25 +08:00
|
|
|
goto no_delete_lock;
|
2008-07-25 00:17:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
btrfs_orphan_del(trans, inode);
|
2008-01-30 04:11:36 +08:00
|
|
|
|
2007-09-17 22:58:06 +08:00
|
|
|
nr = trans->blocks_used;
|
2008-01-30 04:11:36 +08:00
|
|
|
clear_inode(inode);
|
2007-10-16 04:14:19 +08:00
|
|
|
|
2007-06-12 18:35:45 +08:00
|
|
|
btrfs_end_transaction(trans, root);
|
2007-09-17 22:58:06 +08:00
|
|
|
btrfs_btree_balance_dirty(root, nr);
|
2007-06-12 18:35:45 +08:00
|
|
|
return;
|
2007-06-23 02:16:25 +08:00
|
|
|
|
|
|
|
no_delete_lock:
|
2007-09-17 22:58:06 +08:00
|
|
|
nr = trans->blocks_used;
|
2007-06-23 02:16:25 +08:00
|
|
|
btrfs_end_transaction(trans, root);
|
2007-09-17 22:58:06 +08:00
|
|
|
btrfs_btree_balance_dirty(root, nr);
|
2007-06-12 18:35:45 +08:00
|
|
|
no_delete:
|
|
|
|
clear_inode(inode);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* this returns the key found in the dir entry in the location pointer.
|
|
|
|
* If no dir entries were found, location->objectid is 0.
|
|
|
|
*/
|
|
|
|
static int btrfs_inode_by_name(struct inode *dir, struct dentry *dentry,
|
|
|
|
struct btrfs_key *location)
|
|
|
|
{
|
|
|
|
const char *name = dentry->d_name.name;
|
|
|
|
int namelen = dentry->d_name.len;
|
|
|
|
struct btrfs_dir_item *di;
|
|
|
|
struct btrfs_path *path;
|
|
|
|
struct btrfs_root *root = BTRFS_I(dir)->root;
|
2007-10-26 03:48:28 +08:00
|
|
|
int ret = 0;
|
2007-06-12 18:35:45 +08:00
|
|
|
|
|
|
|
path = btrfs_alloc_path();
|
|
|
|
BUG_ON(!path);
|
2007-12-13 03:38:19 +08:00
|
|
|
|
2007-06-12 18:35:45 +08:00
|
|
|
di = btrfs_lookup_dir_item(NULL, root, path, dir->i_ino, name,
|
|
|
|
namelen, 0);
|
2007-10-26 03:48:28 +08:00
|
|
|
if (IS_ERR(di))
|
|
|
|
ret = PTR_ERR(di);
|
2009-01-06 10:25:51 +08:00
|
|
|
|
|
|
|
if (!di || IS_ERR(di))
|
2007-12-13 03:38:19 +08:00
|
|
|
goto out_err;
|
2009-01-06 10:25:51 +08:00
|
|
|
|
2007-10-16 04:14:19 +08:00
|
|
|
btrfs_dir_item_key_to_cpu(path->nodes[0], di, location);
|
2007-06-12 18:35:45 +08:00
|
|
|
out:
|
|
|
|
btrfs_free_path(path);
|
|
|
|
return ret;
|
2007-12-13 03:38:19 +08:00
|
|
|
out_err:
|
|
|
|
location->objectid = 0;
|
|
|
|
goto out;
|
2007-06-12 18:35:45 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* when we hit a tree root in a directory, the btrfs part of the inode
|
|
|
|
* needs to be changed to reflect the root directory of the tree root. This
|
|
|
|
* is kind of like crossing a mount point.
|
|
|
|
*/
|
|
|
|
static int fixup_tree_root_location(struct btrfs_root *root,
|
2009-09-22 03:56:00 +08:00
|
|
|
struct inode *dir,
|
|
|
|
struct dentry *dentry,
|
|
|
|
struct btrfs_key *location,
|
|
|
|
struct btrfs_root **sub_root)
|
2007-06-12 18:35:45 +08:00
|
|
|
{
|
2009-09-22 03:56:00 +08:00
|
|
|
struct btrfs_path *path;
|
|
|
|
struct btrfs_root *new_root;
|
|
|
|
struct btrfs_root_ref *ref;
|
|
|
|
struct extent_buffer *leaf;
|
|
|
|
int ret;
|
|
|
|
int err = 0;
|
2007-06-12 18:35:45 +08:00
|
|
|
|
2009-09-22 03:56:00 +08:00
|
|
|
path = btrfs_alloc_path();
|
|
|
|
if (!path) {
|
|
|
|
err = -ENOMEM;
|
|
|
|
goto out;
|
|
|
|
}
|
2007-06-12 18:35:45 +08:00
|
|
|
|
2009-09-22 03:56:00 +08:00
|
|
|
err = -ENOENT;
|
|
|
|
ret = btrfs_find_root_ref(root->fs_info->tree_root, path,
|
|
|
|
BTRFS_I(dir)->root->root_key.objectid,
|
|
|
|
location->objectid);
|
|
|
|
if (ret) {
|
|
|
|
if (ret < 0)
|
|
|
|
err = ret;
|
|
|
|
goto out;
|
|
|
|
}
|
2007-06-12 18:35:45 +08:00
|
|
|
|
2009-09-22 03:56:00 +08:00
|
|
|
leaf = path->nodes[0];
|
|
|
|
ref = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_root_ref);
|
|
|
|
if (btrfs_root_ref_dirid(leaf, ref) != dir->i_ino ||
|
|
|
|
btrfs_root_ref_name_len(leaf, ref) != dentry->d_name.len)
|
|
|
|
goto out;
|
2007-06-12 18:35:45 +08:00
|
|
|
|
2009-09-22 03:56:00 +08:00
|
|
|
ret = memcmp_extent_buffer(leaf, dentry->d_name.name,
|
|
|
|
(unsigned long)(ref + 1),
|
|
|
|
dentry->d_name.len);
|
|
|
|
if (ret)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
btrfs_release_path(root->fs_info->tree_root, path);
|
|
|
|
|
|
|
|
new_root = btrfs_read_fs_root_no_name(root->fs_info, location);
|
|
|
|
if (IS_ERR(new_root)) {
|
|
|
|
err = PTR_ERR(new_root);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (btrfs_root_refs(&new_root->root_item) == 0) {
|
|
|
|
err = -ENOENT;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
*sub_root = new_root;
|
|
|
|
location->objectid = btrfs_root_dirid(&new_root->root_item);
|
|
|
|
location->type = BTRFS_INODE_ITEM_KEY;
|
|
|
|
location->offset = 0;
|
|
|
|
err = 0;
|
|
|
|
out:
|
|
|
|
btrfs_free_path(path);
|
|
|
|
return err;
|
2007-06-12 18:35:45 +08:00
|
|
|
}
|
|
|
|
|
Btrfs: Mixed back reference (FORWARD ROLLING FORMAT CHANGE)
This commit introduces a new kind of back reference for btrfs metadata.
Once a filesystem has been mounted with this commit, IT WILL NO LONGER
BE MOUNTABLE BY OLDER KERNELS.
When a tree block in subvolume tree is cow'd, the reference counts of all
extents it points to are increased by one. At transaction commit time,
the old root of the subvolume is recorded in a "dead root" data structure,
and the btree it points to is later walked, dropping reference counts
and freeing any blocks where the reference count goes to 0.
The increments done during cow and decrements done after commit cancel out,
and the walk is a very expensive way to go about freeing the blocks that
are no longer referenced by the new btree root. This commit reduces the
transaction overhead by avoiding the need for dead root records.
When a non-shared tree block is cow'd, we free the old block at once, and the
new block inherits old block's references. When a tree block with reference
count > 1 is cow'd, we increase the reference counts of all extents
the new block points to by one, and decrease the old block's reference count by
one.
This dead tree avoidance code removes the need to modify the reference
counts of lower level extents when a non-shared tree block is cow'd.
But we still need to update back ref for all pointers in the block.
This is because the location of the block is recorded in the back ref
item.
We can solve this by introducing a new type of back ref. The new
back ref provides information about pointer's key, level and in which
tree the pointer lives. This information allow us to find the pointer
by searching the tree. The shortcoming of the new back ref is that it
only works for pointers in tree blocks referenced by their owner trees.
This is mostly a problem for snapshots, where resolving one of these
fuzzy back references would be O(number_of_snapshots) and quite slow.
The solution used here is to use the fuzzy back references in the common
case where a given tree block is only referenced by one root,
and use the full back references when multiple roots have a reference
on a given block.
This commit adds per subvolume red-black tree to keep trace of cached
inodes. The red-black tree helps the balancing code to find cached
inodes whose inode numbers within a given range.
This commit improves the balancing code by introducing several data
structures to keep the state of balancing. The most important one
is the back ref cache. It caches how the upper level tree blocks are
referenced. This greatly reduce the overhead of checking back ref.
The improved balancing code scales significantly better with a large
number of snapshots.
This is a very large commit and was written in a number of
pieces. But, they depend heavily on the disk format change and were
squashed together to make sure git bisect didn't end up in a
bad state wrt space balancing or the format change.
Signed-off-by: Yan Zheng <zheng.yan@oracle.com>
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2009-06-10 22:45:14 +08:00
|
|
|
static void inode_tree_add(struct inode *inode)
|
|
|
|
{
|
|
|
|
struct btrfs_root *root = BTRFS_I(inode)->root;
|
|
|
|
struct btrfs_inode *entry;
|
2009-08-21 16:09:44 +08:00
|
|
|
struct rb_node **p;
|
|
|
|
struct rb_node *parent;
|
|
|
|
again:
|
|
|
|
p = &root->inode_tree.rb_node;
|
|
|
|
parent = NULL;
|
Btrfs: Mixed back reference (FORWARD ROLLING FORMAT CHANGE)
This commit introduces a new kind of back reference for btrfs metadata.
Once a filesystem has been mounted with this commit, IT WILL NO LONGER
BE MOUNTABLE BY OLDER KERNELS.
When a tree block in subvolume tree is cow'd, the reference counts of all
extents it points to are increased by one. At transaction commit time,
the old root of the subvolume is recorded in a "dead root" data structure,
and the btree it points to is later walked, dropping reference counts
and freeing any blocks where the reference count goes to 0.
The increments done during cow and decrements done after commit cancel out,
and the walk is a very expensive way to go about freeing the blocks that
are no longer referenced by the new btree root. This commit reduces the
transaction overhead by avoiding the need for dead root records.
When a non-shared tree block is cow'd, we free the old block at once, and the
new block inherits old block's references. When a tree block with reference
count > 1 is cow'd, we increase the reference counts of all extents
the new block points to by one, and decrease the old block's reference count by
one.
This dead tree avoidance code removes the need to modify the reference
counts of lower level extents when a non-shared tree block is cow'd.
But we still need to update back ref for all pointers in the block.
This is because the location of the block is recorded in the back ref
item.
We can solve this by introducing a new type of back ref. The new
back ref provides information about pointer's key, level and in which
tree the pointer lives. This information allow us to find the pointer
by searching the tree. The shortcoming of the new back ref is that it
only works for pointers in tree blocks referenced by their owner trees.
This is mostly a problem for snapshots, where resolving one of these
fuzzy back references would be O(number_of_snapshots) and quite slow.
The solution used here is to use the fuzzy back references in the common
case where a given tree block is only referenced by one root,
and use the full back references when multiple roots have a reference
on a given block.
This commit adds per subvolume red-black tree to keep trace of cached
inodes. The red-black tree helps the balancing code to find cached
inodes whose inode numbers within a given range.
This commit improves the balancing code by introducing several data
structures to keep the state of balancing. The most important one
is the back ref cache. It caches how the upper level tree blocks are
referenced. This greatly reduce the overhead of checking back ref.
The improved balancing code scales significantly better with a large
number of snapshots.
This is a very large commit and was written in a number of
pieces. But, they depend heavily on the disk format change and were
squashed together to make sure git bisect didn't end up in a
bad state wrt space balancing or the format change.
Signed-off-by: Yan Zheng <zheng.yan@oracle.com>
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2009-06-10 22:45:14 +08:00
|
|
|
|
2009-09-22 04:00:26 +08:00
|
|
|
if (hlist_unhashed(&inode->i_hash))
|
|
|
|
return;
|
|
|
|
|
Btrfs: Mixed back reference (FORWARD ROLLING FORMAT CHANGE)
This commit introduces a new kind of back reference for btrfs metadata.
Once a filesystem has been mounted with this commit, IT WILL NO LONGER
BE MOUNTABLE BY OLDER KERNELS.
When a tree block in subvolume tree is cow'd, the reference counts of all
extents it points to are increased by one. At transaction commit time,
the old root of the subvolume is recorded in a "dead root" data structure,
and the btree it points to is later walked, dropping reference counts
and freeing any blocks where the reference count goes to 0.
The increments done during cow and decrements done after commit cancel out,
and the walk is a very expensive way to go about freeing the blocks that
are no longer referenced by the new btree root. This commit reduces the
transaction overhead by avoiding the need for dead root records.
When a non-shared tree block is cow'd, we free the old block at once, and the
new block inherits old block's references. When a tree block with reference
count > 1 is cow'd, we increase the reference counts of all extents
the new block points to by one, and decrease the old block's reference count by
one.
This dead tree avoidance code removes the need to modify the reference
counts of lower level extents when a non-shared tree block is cow'd.
But we still need to update back ref for all pointers in the block.
This is because the location of the block is recorded in the back ref
item.
We can solve this by introducing a new type of back ref. The new
back ref provides information about pointer's key, level and in which
tree the pointer lives. This information allow us to find the pointer
by searching the tree. The shortcoming of the new back ref is that it
only works for pointers in tree blocks referenced by their owner trees.
This is mostly a problem for snapshots, where resolving one of these
fuzzy back references would be O(number_of_snapshots) and quite slow.
The solution used here is to use the fuzzy back references in the common
case where a given tree block is only referenced by one root,
and use the full back references when multiple roots have a reference
on a given block.
This commit adds per subvolume red-black tree to keep trace of cached
inodes. The red-black tree helps the balancing code to find cached
inodes whose inode numbers within a given range.
This commit improves the balancing code by introducing several data
structures to keep the state of balancing. The most important one
is the back ref cache. It caches how the upper level tree blocks are
referenced. This greatly reduce the overhead of checking back ref.
The improved balancing code scales significantly better with a large
number of snapshots.
This is a very large commit and was written in a number of
pieces. But, they depend heavily on the disk format change and were
squashed together to make sure git bisect didn't end up in a
bad state wrt space balancing or the format change.
Signed-off-by: Yan Zheng <zheng.yan@oracle.com>
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2009-06-10 22:45:14 +08:00
|
|
|
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)
|
2009-08-21 16:09:44 +08:00
|
|
|
p = &parent->rb_left;
|
Btrfs: Mixed back reference (FORWARD ROLLING FORMAT CHANGE)
This commit introduces a new kind of back reference for btrfs metadata.
Once a filesystem has been mounted with this commit, IT WILL NO LONGER
BE MOUNTABLE BY OLDER KERNELS.
When a tree block in subvolume tree is cow'd, the reference counts of all
extents it points to are increased by one. At transaction commit time,
the old root of the subvolume is recorded in a "dead root" data structure,
and the btree it points to is later walked, dropping reference counts
and freeing any blocks where the reference count goes to 0.
The increments done during cow and decrements done after commit cancel out,
and the walk is a very expensive way to go about freeing the blocks that
are no longer referenced by the new btree root. This commit reduces the
transaction overhead by avoiding the need for dead root records.
When a non-shared tree block is cow'd, we free the old block at once, and the
new block inherits old block's references. When a tree block with reference
count > 1 is cow'd, we increase the reference counts of all extents
the new block points to by one, and decrease the old block's reference count by
one.
This dead tree avoidance code removes the need to modify the reference
counts of lower level extents when a non-shared tree block is cow'd.
But we still need to update back ref for all pointers in the block.
This is because the location of the block is recorded in the back ref
item.
We can solve this by introducing a new type of back ref. The new
back ref provides information about pointer's key, level and in which
tree the pointer lives. This information allow us to find the pointer
by searching the tree. The shortcoming of the new back ref is that it
only works for pointers in tree blocks referenced by their owner trees.
This is mostly a problem for snapshots, where resolving one of these
fuzzy back references would be O(number_of_snapshots) and quite slow.
The solution used here is to use the fuzzy back references in the common
case where a given tree block is only referenced by one root,
and use the full back references when multiple roots have a reference
on a given block.
This commit adds per subvolume red-black tree to keep trace of cached
inodes. The red-black tree helps the balancing code to find cached
inodes whose inode numbers within a given range.
This commit improves the balancing code by introducing several data
structures to keep the state of balancing. The most important one
is the back ref cache. It caches how the upper level tree blocks are
referenced. This greatly reduce the overhead of checking back ref.
The improved balancing code scales significantly better with a large
number of snapshots.
This is a very large commit and was written in a number of
pieces. But, they depend heavily on the disk format change and were
squashed together to make sure git bisect didn't end up in a
bad state wrt space balancing or the format change.
Signed-off-by: Yan Zheng <zheng.yan@oracle.com>
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2009-06-10 22:45:14 +08:00
|
|
|
else if (inode->i_ino > entry->vfs_inode.i_ino)
|
2009-08-21 16:09:44 +08:00
|
|
|
p = &parent->rb_right;
|
Btrfs: Mixed back reference (FORWARD ROLLING FORMAT CHANGE)
This commit introduces a new kind of back reference for btrfs metadata.
Once a filesystem has been mounted with this commit, IT WILL NO LONGER
BE MOUNTABLE BY OLDER KERNELS.
When a tree block in subvolume tree is cow'd, the reference counts of all
extents it points to are increased by one. At transaction commit time,
the old root of the subvolume is recorded in a "dead root" data structure,
and the btree it points to is later walked, dropping reference counts
and freeing any blocks where the reference count goes to 0.
The increments done during cow and decrements done after commit cancel out,
and the walk is a very expensive way to go about freeing the blocks that
are no longer referenced by the new btree root. This commit reduces the
transaction overhead by avoiding the need for dead root records.
When a non-shared tree block is cow'd, we free the old block at once, and the
new block inherits old block's references. When a tree block with reference
count > 1 is cow'd, we increase the reference counts of all extents
the new block points to by one, and decrease the old block's reference count by
one.
This dead tree avoidance code removes the need to modify the reference
counts of lower level extents when a non-shared tree block is cow'd.
But we still need to update back ref for all pointers in the block.
This is because the location of the block is recorded in the back ref
item.
We can solve this by introducing a new type of back ref. The new
back ref provides information about pointer's key, level and in which
tree the pointer lives. This information allow us to find the pointer
by searching the tree. The shortcoming of the new back ref is that it
only works for pointers in tree blocks referenced by their owner trees.
This is mostly a problem for snapshots, where resolving one of these
fuzzy back references would be O(number_of_snapshots) and quite slow.
The solution used here is to use the fuzzy back references in the common
case where a given tree block is only referenced by one root,
and use the full back references when multiple roots have a reference
on a given block.
This commit adds per subvolume red-black tree to keep trace of cached
inodes. The red-black tree helps the balancing code to find cached
inodes whose inode numbers within a given range.
This commit improves the balancing code by introducing several data
structures to keep the state of balancing. The most important one
is the back ref cache. It caches how the upper level tree blocks are
referenced. This greatly reduce the overhead of checking back ref.
The improved balancing code scales significantly better with a large
number of snapshots.
This is a very large commit and was written in a number of
pieces. But, they depend heavily on the disk format change and were
squashed together to make sure git bisect didn't end up in a
bad state wrt space balancing or the format change.
Signed-off-by: Yan Zheng <zheng.yan@oracle.com>
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2009-06-10 22:45:14 +08:00
|
|
|
else {
|
|
|
|
WARN_ON(!(entry->vfs_inode.i_state &
|
|
|
|
(I_WILL_FREE | I_FREEING | I_CLEAR)));
|
2009-08-21 16:09:44 +08:00
|
|
|
rb_erase(parent, &root->inode_tree);
|
|
|
|
RB_CLEAR_NODE(parent);
|
|
|
|
spin_unlock(&root->inode_lock);
|
|
|
|
goto again;
|
Btrfs: Mixed back reference (FORWARD ROLLING FORMAT CHANGE)
This commit introduces a new kind of back reference for btrfs metadata.
Once a filesystem has been mounted with this commit, IT WILL NO LONGER
BE MOUNTABLE BY OLDER KERNELS.
When a tree block in subvolume tree is cow'd, the reference counts of all
extents it points to are increased by one. At transaction commit time,
the old root of the subvolume is recorded in a "dead root" data structure,
and the btree it points to is later walked, dropping reference counts
and freeing any blocks where the reference count goes to 0.
The increments done during cow and decrements done after commit cancel out,
and the walk is a very expensive way to go about freeing the blocks that
are no longer referenced by the new btree root. This commit reduces the
transaction overhead by avoiding the need for dead root records.
When a non-shared tree block is cow'd, we free the old block at once, and the
new block inherits old block's references. When a tree block with reference
count > 1 is cow'd, we increase the reference counts of all extents
the new block points to by one, and decrease the old block's reference count by
one.
This dead tree avoidance code removes the need to modify the reference
counts of lower level extents when a non-shared tree block is cow'd.
But we still need to update back ref for all pointers in the block.
This is because the location of the block is recorded in the back ref
item.
We can solve this by introducing a new type of back ref. The new
back ref provides information about pointer's key, level and in which
tree the pointer lives. This information allow us to find the pointer
by searching the tree. The shortcoming of the new back ref is that it
only works for pointers in tree blocks referenced by their owner trees.
This is mostly a problem for snapshots, where resolving one of these
fuzzy back references would be O(number_of_snapshots) and quite slow.
The solution used here is to use the fuzzy back references in the common
case where a given tree block is only referenced by one root,
and use the full back references when multiple roots have a reference
on a given block.
This commit adds per subvolume red-black tree to keep trace of cached
inodes. The red-black tree helps the balancing code to find cached
inodes whose inode numbers within a given range.
This commit improves the balancing code by introducing several data
structures to keep the state of balancing. The most important one
is the back ref cache. It caches how the upper level tree blocks are
referenced. This greatly reduce the overhead of checking back ref.
The improved balancing code scales significantly better with a large
number of snapshots.
This is a very large commit and was written in a number of
pieces. But, they depend heavily on the disk format change and were
squashed together to make sure git bisect didn't end up in a
bad state wrt space balancing or the format change.
Signed-off-by: Yan Zheng <zheng.yan@oracle.com>
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2009-06-10 22:45:14 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
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;
|
2009-09-22 04:00:26 +08:00
|
|
|
int empty = 0;
|
Btrfs: Mixed back reference (FORWARD ROLLING FORMAT CHANGE)
This commit introduces a new kind of back reference for btrfs metadata.
Once a filesystem has been mounted with this commit, IT WILL NO LONGER
BE MOUNTABLE BY OLDER KERNELS.
When a tree block in subvolume tree is cow'd, the reference counts of all
extents it points to are increased by one. At transaction commit time,
the old root of the subvolume is recorded in a "dead root" data structure,
and the btree it points to is later walked, dropping reference counts
and freeing any blocks where the reference count goes to 0.
The increments done during cow and decrements done after commit cancel out,
and the walk is a very expensive way to go about freeing the blocks that
are no longer referenced by the new btree root. This commit reduces the
transaction overhead by avoiding the need for dead root records.
When a non-shared tree block is cow'd, we free the old block at once, and the
new block inherits old block's references. When a tree block with reference
count > 1 is cow'd, we increase the reference counts of all extents
the new block points to by one, and decrease the old block's reference count by
one.
This dead tree avoidance code removes the need to modify the reference
counts of lower level extents when a non-shared tree block is cow'd.
But we still need to update back ref for all pointers in the block.
This is because the location of the block is recorded in the back ref
item.
We can solve this by introducing a new type of back ref. The new
back ref provides information about pointer's key, level and in which
tree the pointer lives. This information allow us to find the pointer
by searching the tree. The shortcoming of the new back ref is that it
only works for pointers in tree blocks referenced by their owner trees.
This is mostly a problem for snapshots, where resolving one of these
fuzzy back references would be O(number_of_snapshots) and quite slow.
The solution used here is to use the fuzzy back references in the common
case where a given tree block is only referenced by one root,
and use the full back references when multiple roots have a reference
on a given block.
This commit adds per subvolume red-black tree to keep trace of cached
inodes. The red-black tree helps the balancing code to find cached
inodes whose inode numbers within a given range.
This commit improves the balancing code by introducing several data
structures to keep the state of balancing. The most important one
is the back ref cache. It caches how the upper level tree blocks are
referenced. This greatly reduce the overhead of checking back ref.
The improved balancing code scales significantly better with a large
number of snapshots.
This is a very large commit and was written in a number of
pieces. But, they depend heavily on the disk format change and were
squashed together to make sure git bisect didn't end up in a
bad state wrt space balancing or the format change.
Signed-off-by: Yan Zheng <zheng.yan@oracle.com>
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2009-06-10 22:45:14 +08:00
|
|
|
|
2009-08-21 16:09:44 +08:00
|
|
|
spin_lock(&root->inode_lock);
|
Btrfs: Mixed back reference (FORWARD ROLLING FORMAT CHANGE)
This commit introduces a new kind of back reference for btrfs metadata.
Once a filesystem has been mounted with this commit, IT WILL NO LONGER
BE MOUNTABLE BY OLDER KERNELS.
When a tree block in subvolume tree is cow'd, the reference counts of all
extents it points to are increased by one. At transaction commit time,
the old root of the subvolume is recorded in a "dead root" data structure,
and the btree it points to is later walked, dropping reference counts
and freeing any blocks where the reference count goes to 0.
The increments done during cow and decrements done after commit cancel out,
and the walk is a very expensive way to go about freeing the blocks that
are no longer referenced by the new btree root. This commit reduces the
transaction overhead by avoiding the need for dead root records.
When a non-shared tree block is cow'd, we free the old block at once, and the
new block inherits old block's references. When a tree block with reference
count > 1 is cow'd, we increase the reference counts of all extents
the new block points to by one, and decrease the old block's reference count by
one.
This dead tree avoidance code removes the need to modify the reference
counts of lower level extents when a non-shared tree block is cow'd.
But we still need to update back ref for all pointers in the block.
This is because the location of the block is recorded in the back ref
item.
We can solve this by introducing a new type of back ref. The new
back ref provides information about pointer's key, level and in which
tree the pointer lives. This information allow us to find the pointer
by searching the tree. The shortcoming of the new back ref is that it
only works for pointers in tree blocks referenced by their owner trees.
This is mostly a problem for snapshots, where resolving one of these
fuzzy back references would be O(number_of_snapshots) and quite slow.
The solution used here is to use the fuzzy back references in the common
case where a given tree block is only referenced by one root,
and use the full back references when multiple roots have a reference
on a given block.
This commit adds per subvolume red-black tree to keep trace of cached
inodes. The red-black tree helps the balancing code to find cached
inodes whose inode numbers within a given range.
This commit improves the balancing code by introducing several data
structures to keep the state of balancing. The most important one
is the back ref cache. It caches how the upper level tree blocks are
referenced. This greatly reduce the overhead of checking back ref.
The improved balancing code scales significantly better with a large
number of snapshots.
This is a very large commit and was written in a number of
pieces. But, they depend heavily on the disk format change and were
squashed together to make sure git bisect didn't end up in a
bad state wrt space balancing or the format change.
Signed-off-by: Yan Zheng <zheng.yan@oracle.com>
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2009-06-10 22:45:14 +08:00
|
|
|
if (!RB_EMPTY_NODE(&BTRFS_I(inode)->rb_node)) {
|
|
|
|
rb_erase(&BTRFS_I(inode)->rb_node, &root->inode_tree);
|
|
|
|
RB_CLEAR_NODE(&BTRFS_I(inode)->rb_node);
|
2009-09-22 04:00:26 +08:00
|
|
|
empty = RB_EMPTY_ROOT(&root->inode_tree);
|
Btrfs: Mixed back reference (FORWARD ROLLING FORMAT CHANGE)
This commit introduces a new kind of back reference for btrfs metadata.
Once a filesystem has been mounted with this commit, IT WILL NO LONGER
BE MOUNTABLE BY OLDER KERNELS.
When a tree block in subvolume tree is cow'd, the reference counts of all
extents it points to are increased by one. At transaction commit time,
the old root of the subvolume is recorded in a "dead root" data structure,
and the btree it points to is later walked, dropping reference counts
and freeing any blocks where the reference count goes to 0.
The increments done during cow and decrements done after commit cancel out,
and the walk is a very expensive way to go about freeing the blocks that
are no longer referenced by the new btree root. This commit reduces the
transaction overhead by avoiding the need for dead root records.
When a non-shared tree block is cow'd, we free the old block at once, and the
new block inherits old block's references. When a tree block with reference
count > 1 is cow'd, we increase the reference counts of all extents
the new block points to by one, and decrease the old block's reference count by
one.
This dead tree avoidance code removes the need to modify the reference
counts of lower level extents when a non-shared tree block is cow'd.
But we still need to update back ref for all pointers in the block.
This is because the location of the block is recorded in the back ref
item.
We can solve this by introducing a new type of back ref. The new
back ref provides information about pointer's key, level and in which
tree the pointer lives. This information allow us to find the pointer
by searching the tree. The shortcoming of the new back ref is that it
only works for pointers in tree blocks referenced by their owner trees.
This is mostly a problem for snapshots, where resolving one of these
fuzzy back references would be O(number_of_snapshots) and quite slow.
The solution used here is to use the fuzzy back references in the common
case where a given tree block is only referenced by one root,
and use the full back references when multiple roots have a reference
on a given block.
This commit adds per subvolume red-black tree to keep trace of cached
inodes. The red-black tree helps the balancing code to find cached
inodes whose inode numbers within a given range.
This commit improves the balancing code by introducing several data
structures to keep the state of balancing. The most important one
is the back ref cache. It caches how the upper level tree blocks are
referenced. This greatly reduce the overhead of checking back ref.
The improved balancing code scales significantly better with a large
number of snapshots.
This is a very large commit and was written in a number of
pieces. But, they depend heavily on the disk format change and were
squashed together to make sure git bisect didn't end up in a
bad state wrt space balancing or the format change.
Signed-off-by: Yan Zheng <zheng.yan@oracle.com>
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2009-06-10 22:45:14 +08:00
|
|
|
}
|
2009-08-21 16:09:44 +08:00
|
|
|
spin_unlock(&root->inode_lock);
|
2009-09-22 04:00:26 +08:00
|
|
|
|
|
|
|
if (empty && btrfs_root_refs(&root->root_item) == 0) {
|
|
|
|
synchronize_srcu(&root->fs_info->subvol_srcu);
|
|
|
|
spin_lock(&root->inode_lock);
|
|
|
|
empty = RB_EMPTY_ROOT(&root->inode_tree);
|
|
|
|
spin_unlock(&root->inode_lock);
|
|
|
|
if (empty)
|
|
|
|
btrfs_add_dead_root(root);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int btrfs_invalidate_inodes(struct btrfs_root *root)
|
|
|
|
{
|
|
|
|
struct rb_node *node;
|
|
|
|
struct rb_node *prev;
|
|
|
|
struct btrfs_inode *entry;
|
|
|
|
struct inode *inode;
|
|
|
|
u64 objectid = 0;
|
|
|
|
|
|
|
|
WARN_ON(btrfs_root_refs(&root->root_item) != 0);
|
|
|
|
|
|
|
|
spin_lock(&root->inode_lock);
|
|
|
|
again:
|
|
|
|
node = root->inode_tree.rb_node;
|
|
|
|
prev = NULL;
|
|
|
|
while (node) {
|
|
|
|
prev = node;
|
|
|
|
entry = rb_entry(node, struct btrfs_inode, rb_node);
|
|
|
|
|
|
|
|
if (objectid < entry->vfs_inode.i_ino)
|
|
|
|
node = node->rb_left;
|
|
|
|
else if (objectid > entry->vfs_inode.i_ino)
|
|
|
|
node = node->rb_right;
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!node) {
|
|
|
|
while (prev) {
|
|
|
|
entry = rb_entry(prev, struct btrfs_inode, rb_node);
|
|
|
|
if (objectid <= entry->vfs_inode.i_ino) {
|
|
|
|
node = prev;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
prev = rb_next(prev);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
while (node) {
|
|
|
|
entry = rb_entry(node, struct btrfs_inode, rb_node);
|
|
|
|
objectid = entry->vfs_inode.i_ino + 1;
|
|
|
|
inode = igrab(&entry->vfs_inode);
|
|
|
|
if (inode) {
|
|
|
|
spin_unlock(&root->inode_lock);
|
|
|
|
if (atomic_read(&inode->i_count) > 1)
|
|
|
|
d_prune_aliases(inode);
|
|
|
|
/*
|
|
|
|
* btrfs_drop_inode will remove it from
|
|
|
|
* the inode cache when its usage count
|
|
|
|
* hits zero.
|
|
|
|
*/
|
|
|
|
iput(inode);
|
|
|
|
cond_resched();
|
|
|
|
spin_lock(&root->inode_lock);
|
|
|
|
goto again;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cond_resched_lock(&root->inode_lock))
|
|
|
|
goto again;
|
|
|
|
|
|
|
|
node = rb_next(node);
|
|
|
|
}
|
|
|
|
spin_unlock(&root->inode_lock);
|
|
|
|
return 0;
|
Btrfs: Mixed back reference (FORWARD ROLLING FORMAT CHANGE)
This commit introduces a new kind of back reference for btrfs metadata.
Once a filesystem has been mounted with this commit, IT WILL NO LONGER
BE MOUNTABLE BY OLDER KERNELS.
When a tree block in subvolume tree is cow'd, the reference counts of all
extents it points to are increased by one. At transaction commit time,
the old root of the subvolume is recorded in a "dead root" data structure,
and the btree it points to is later walked, dropping reference counts
and freeing any blocks where the reference count goes to 0.
The increments done during cow and decrements done after commit cancel out,
and the walk is a very expensive way to go about freeing the blocks that
are no longer referenced by the new btree root. This commit reduces the
transaction overhead by avoiding the need for dead root records.
When a non-shared tree block is cow'd, we free the old block at once, and the
new block inherits old block's references. When a tree block with reference
count > 1 is cow'd, we increase the reference counts of all extents
the new block points to by one, and decrease the old block's reference count by
one.
This dead tree avoidance code removes the need to modify the reference
counts of lower level extents when a non-shared tree block is cow'd.
But we still need to update back ref for all pointers in the block.
This is because the location of the block is recorded in the back ref
item.
We can solve this by introducing a new type of back ref. The new
back ref provides information about pointer's key, level and in which
tree the pointer lives. This information allow us to find the pointer
by searching the tree. The shortcoming of the new back ref is that it
only works for pointers in tree blocks referenced by their owner trees.
This is mostly a problem for snapshots, where resolving one of these
fuzzy back references would be O(number_of_snapshots) and quite slow.
The solution used here is to use the fuzzy back references in the common
case where a given tree block is only referenced by one root,
and use the full back references when multiple roots have a reference
on a given block.
This commit adds per subvolume red-black tree to keep trace of cached
inodes. The red-black tree helps the balancing code to find cached
inodes whose inode numbers within a given range.
This commit improves the balancing code by introducing several data
structures to keep the state of balancing. The most important one
is the back ref cache. It caches how the upper level tree blocks are
referenced. This greatly reduce the overhead of checking back ref.
The improved balancing code scales significantly better with a large
number of snapshots.
This is a very large commit and was written in a number of
pieces. But, they depend heavily on the disk format change and were
squashed together to make sure git bisect didn't end up in a
bad state wrt space balancing or the format change.
Signed-off-by: Yan Zheng <zheng.yan@oracle.com>
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2009-06-10 22:45:14 +08:00
|
|
|
}
|
|
|
|
|
2008-09-06 04:13:11 +08:00
|
|
|
static noinline void init_btrfs_i(struct inode *inode)
|
2007-06-12 18:35:45 +08:00
|
|
|
{
|
2008-09-06 04:13:11 +08:00
|
|
|
struct btrfs_inode *bi = BTRFS_I(inode);
|
|
|
|
|
|
|
|
bi->generation = 0;
|
2008-12-09 05:40:21 +08:00
|
|
|
bi->sequence = 0;
|
2008-09-06 04:13:11 +08:00
|
|
|
bi->last_trans = 0;
|
|
|
|
bi->logged_trans = 0;
|
|
|
|
bi->delalloc_bytes = 0;
|
2009-02-21 00:00:09 +08:00
|
|
|
bi->reserved_bytes = 0;
|
2008-09-06 04:13:11 +08:00
|
|
|
bi->disk_i_size = 0;
|
|
|
|
bi->flags = 0;
|
|
|
|
bi->index_cnt = (u64)-1;
|
2009-03-24 22:24:20 +08:00
|
|
|
bi->last_unlink_trans = 0;
|
2009-05-15 01:10:02 +08:00
|
|
|
bi->ordered_data_close = 0;
|
2008-01-25 05:13:08 +08:00
|
|
|
extent_map_tree_init(&BTRFS_I(inode)->extent_tree, GFP_NOFS);
|
|
|
|
extent_io_tree_init(&BTRFS_I(inode)->io_tree,
|
2007-08-28 04:49:44 +08:00
|
|
|
inode->i_mapping, GFP_NOFS);
|
2008-04-10 04:28:12 +08:00
|
|
|
extent_io_tree_init(&BTRFS_I(inode)->io_failure_tree,
|
|
|
|
inode->i_mapping, GFP_NOFS);
|
2008-08-05 11:17:27 +08:00
|
|
|
INIT_LIST_HEAD(&BTRFS_I(inode)->delalloc_inodes);
|
2009-04-01 01:27:11 +08:00
|
|
|
INIT_LIST_HEAD(&BTRFS_I(inode)->ordered_operations);
|
Btrfs: Mixed back reference (FORWARD ROLLING FORMAT CHANGE)
This commit introduces a new kind of back reference for btrfs metadata.
Once a filesystem has been mounted with this commit, IT WILL NO LONGER
BE MOUNTABLE BY OLDER KERNELS.
When a tree block in subvolume tree is cow'd, the reference counts of all
extents it points to are increased by one. At transaction commit time,
the old root of the subvolume is recorded in a "dead root" data structure,
and the btree it points to is later walked, dropping reference counts
and freeing any blocks where the reference count goes to 0.
The increments done during cow and decrements done after commit cancel out,
and the walk is a very expensive way to go about freeing the blocks that
are no longer referenced by the new btree root. This commit reduces the
transaction overhead by avoiding the need for dead root records.
When a non-shared tree block is cow'd, we free the old block at once, and the
new block inherits old block's references. When a tree block with reference
count > 1 is cow'd, we increase the reference counts of all extents
the new block points to by one, and decrease the old block's reference count by
one.
This dead tree avoidance code removes the need to modify the reference
counts of lower level extents when a non-shared tree block is cow'd.
But we still need to update back ref for all pointers in the block.
This is because the location of the block is recorded in the back ref
item.
We can solve this by introducing a new type of back ref. The new
back ref provides information about pointer's key, level and in which
tree the pointer lives. This information allow us to find the pointer
by searching the tree. The shortcoming of the new back ref is that it
only works for pointers in tree blocks referenced by their owner trees.
This is mostly a problem for snapshots, where resolving one of these
fuzzy back references would be O(number_of_snapshots) and quite slow.
The solution used here is to use the fuzzy back references in the common
case where a given tree block is only referenced by one root,
and use the full back references when multiple roots have a reference
on a given block.
This commit adds per subvolume red-black tree to keep trace of cached
inodes. The red-black tree helps the balancing code to find cached
inodes whose inode numbers within a given range.
This commit improves the balancing code by introducing several data
structures to keep the state of balancing. The most important one
is the back ref cache. It caches how the upper level tree blocks are
referenced. This greatly reduce the overhead of checking back ref.
The improved balancing code scales significantly better with a large
number of snapshots.
This is a very large commit and was written in a number of
pieces. But, they depend heavily on the disk format change and were
squashed together to make sure git bisect didn't end up in a
bad state wrt space balancing or the format change.
Signed-off-by: Yan Zheng <zheng.yan@oracle.com>
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2009-06-10 22:45:14 +08:00
|
|
|
RB_CLEAR_NODE(&BTRFS_I(inode)->rb_node);
|
2008-07-18 00:54:15 +08:00
|
|
|
btrfs_ordered_inode_tree_init(&BTRFS_I(inode)->ordered_tree);
|
2008-07-18 00:54:40 +08:00
|
|
|
mutex_init(&BTRFS_I(inode)->extent_mutex);
|
2008-09-06 04:13:11 +08:00
|
|
|
mutex_init(&BTRFS_I(inode)->log_mutex);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int btrfs_init_locked_inode(struct inode *inode, void *p)
|
|
|
|
{
|
|
|
|
struct btrfs_iget_args *args = p;
|
|
|
|
inode->i_ino = args->ino;
|
|
|
|
init_btrfs_i(inode);
|
|
|
|
BTRFS_I(inode)->root = args->root;
|
2009-02-21 00:00:09 +08:00
|
|
|
btrfs_set_inode_space_info(args->root, inode);
|
2007-06-12 18:35:45 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int btrfs_find_actor(struct inode *inode, void *opaque)
|
|
|
|
{
|
|
|
|
struct btrfs_iget_args *args = opaque;
|
2009-01-06 10:25:51 +08:00
|
|
|
return args->ino == inode->i_ino &&
|
|
|
|
args->root == BTRFS_I(inode)->root;
|
2007-06-12 18:35:45 +08:00
|
|
|
}
|
|
|
|
|
Btrfs: Mixed back reference (FORWARD ROLLING FORMAT CHANGE)
This commit introduces a new kind of back reference for btrfs metadata.
Once a filesystem has been mounted with this commit, IT WILL NO LONGER
BE MOUNTABLE BY OLDER KERNELS.
When a tree block in subvolume tree is cow'd, the reference counts of all
extents it points to are increased by one. At transaction commit time,
the old root of the subvolume is recorded in a "dead root" data structure,
and the btree it points to is later walked, dropping reference counts
and freeing any blocks where the reference count goes to 0.
The increments done during cow and decrements done after commit cancel out,
and the walk is a very expensive way to go about freeing the blocks that
are no longer referenced by the new btree root. This commit reduces the
transaction overhead by avoiding the need for dead root records.
When a non-shared tree block is cow'd, we free the old block at once, and the
new block inherits old block's references. When a tree block with reference
count > 1 is cow'd, we increase the reference counts of all extents
the new block points to by one, and decrease the old block's reference count by
one.
This dead tree avoidance code removes the need to modify the reference
counts of lower level extents when a non-shared tree block is cow'd.
But we still need to update back ref for all pointers in the block.
This is because the location of the block is recorded in the back ref
item.
We can solve this by introducing a new type of back ref. The new
back ref provides information about pointer's key, level and in which
tree the pointer lives. This information allow us to find the pointer
by searching the tree. The shortcoming of the new back ref is that it
only works for pointers in tree blocks referenced by their owner trees.
This is mostly a problem for snapshots, where resolving one of these
fuzzy back references would be O(number_of_snapshots) and quite slow.
The solution used here is to use the fuzzy back references in the common
case where a given tree block is only referenced by one root,
and use the full back references when multiple roots have a reference
on a given block.
This commit adds per subvolume red-black tree to keep trace of cached
inodes. The red-black tree helps the balancing code to find cached
inodes whose inode numbers within a given range.
This commit improves the balancing code by introducing several data
structures to keep the state of balancing. The most important one
is the back ref cache. It caches how the upper level tree blocks are
referenced. This greatly reduce the overhead of checking back ref.
The improved balancing code scales significantly better with a large
number of snapshots.
This is a very large commit and was written in a number of
pieces. But, they depend heavily on the disk format change and were
squashed together to make sure git bisect didn't end up in a
bad state wrt space balancing or the format change.
Signed-off-by: Yan Zheng <zheng.yan@oracle.com>
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2009-06-10 22:45:14 +08:00
|
|
|
static struct inode *btrfs_iget_locked(struct super_block *s,
|
|
|
|
u64 objectid,
|
|
|
|
struct btrfs_root *root)
|
2007-06-12 18:35:45 +08:00
|
|
|
{
|
|
|
|
struct inode *inode;
|
|
|
|
struct btrfs_iget_args args;
|
|
|
|
args.ino = objectid;
|
|
|
|
args.root = root;
|
|
|
|
|
|
|
|
inode = iget5_locked(s, objectid, btrfs_find_actor,
|
|
|
|
btrfs_init_locked_inode,
|
|
|
|
(void *)&args);
|
|
|
|
return inode;
|
|
|
|
}
|
|
|
|
|
2008-07-21 04:31:04 +08:00
|
|
|
/* Get an inode object given its location and corresponding root.
|
|
|
|
* Returns in *is_new if the inode was read from disk
|
|
|
|
*/
|
|
|
|
struct inode *btrfs_iget(struct super_block *s, struct btrfs_key *location,
|
Btrfs: Mixed back reference (FORWARD ROLLING FORMAT CHANGE)
This commit introduces a new kind of back reference for btrfs metadata.
Once a filesystem has been mounted with this commit, IT WILL NO LONGER
BE MOUNTABLE BY OLDER KERNELS.
When a tree block in subvolume tree is cow'd, the reference counts of all
extents it points to are increased by one. At transaction commit time,
the old root of the subvolume is recorded in a "dead root" data structure,
and the btree it points to is later walked, dropping reference counts
and freeing any blocks where the reference count goes to 0.
The increments done during cow and decrements done after commit cancel out,
and the walk is a very expensive way to go about freeing the blocks that
are no longer referenced by the new btree root. This commit reduces the
transaction overhead by avoiding the need for dead root records.
When a non-shared tree block is cow'd, we free the old block at once, and the
new block inherits old block's references. When a tree block with reference
count > 1 is cow'd, we increase the reference counts of all extents
the new block points to by one, and decrease the old block's reference count by
one.
This dead tree avoidance code removes the need to modify the reference
counts of lower level extents when a non-shared tree block is cow'd.
But we still need to update back ref for all pointers in the block.
This is because the location of the block is recorded in the back ref
item.
We can solve this by introducing a new type of back ref. The new
back ref provides information about pointer's key, level and in which
tree the pointer lives. This information allow us to find the pointer
by searching the tree. The shortcoming of the new back ref is that it
only works for pointers in tree blocks referenced by their owner trees.
This is mostly a problem for snapshots, where resolving one of these
fuzzy back references would be O(number_of_snapshots) and quite slow.
The solution used here is to use the fuzzy back references in the common
case where a given tree block is only referenced by one root,
and use the full back references when multiple roots have a reference
on a given block.
This commit adds per subvolume red-black tree to keep trace of cached
inodes. The red-black tree helps the balancing code to find cached
inodes whose inode numbers within a given range.
This commit improves the balancing code by introducing several data
structures to keep the state of balancing. The most important one
is the back ref cache. It caches how the upper level tree blocks are
referenced. This greatly reduce the overhead of checking back ref.
The improved balancing code scales significantly better with a large
number of snapshots.
This is a very large commit and was written in a number of
pieces. But, they depend heavily on the disk format change and were
squashed together to make sure git bisect didn't end up in a
bad state wrt space balancing or the format change.
Signed-off-by: Yan Zheng <zheng.yan@oracle.com>
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2009-06-10 22:45:14 +08:00
|
|
|
struct btrfs_root *root)
|
2008-07-21 04:31:04 +08:00
|
|
|
{
|
|
|
|
struct inode *inode;
|
|
|
|
|
|
|
|
inode = btrfs_iget_locked(s, location->objectid, root);
|
|
|
|
if (!inode)
|
Btrfs: Mixed back reference (FORWARD ROLLING FORMAT CHANGE)
This commit introduces a new kind of back reference for btrfs metadata.
Once a filesystem has been mounted with this commit, IT WILL NO LONGER
BE MOUNTABLE BY OLDER KERNELS.
When a tree block in subvolume tree is cow'd, the reference counts of all
extents it points to are increased by one. At transaction commit time,
the old root of the subvolume is recorded in a "dead root" data structure,
and the btree it points to is later walked, dropping reference counts
and freeing any blocks where the reference count goes to 0.
The increments done during cow and decrements done after commit cancel out,
and the walk is a very expensive way to go about freeing the blocks that
are no longer referenced by the new btree root. This commit reduces the
transaction overhead by avoiding the need for dead root records.
When a non-shared tree block is cow'd, we free the old block at once, and the
new block inherits old block's references. When a tree block with reference
count > 1 is cow'd, we increase the reference counts of all extents
the new block points to by one, and decrease the old block's reference count by
one.
This dead tree avoidance code removes the need to modify the reference
counts of lower level extents when a non-shared tree block is cow'd.
But we still need to update back ref for all pointers in the block.
This is because the location of the block is recorded in the back ref
item.
We can solve this by introducing a new type of back ref. The new
back ref provides information about pointer's key, level and in which
tree the pointer lives. This information allow us to find the pointer
by searching the tree. The shortcoming of the new back ref is that it
only works for pointers in tree blocks referenced by their owner trees.
This is mostly a problem for snapshots, where resolving one of these
fuzzy back references would be O(number_of_snapshots) and quite slow.
The solution used here is to use the fuzzy back references in the common
case where a given tree block is only referenced by one root,
and use the full back references when multiple roots have a reference
on a given block.
This commit adds per subvolume red-black tree to keep trace of cached
inodes. The red-black tree helps the balancing code to find cached
inodes whose inode numbers within a given range.
This commit improves the balancing code by introducing several data
structures to keep the state of balancing. The most important one
is the back ref cache. It caches how the upper level tree blocks are
referenced. This greatly reduce the overhead of checking back ref.
The improved balancing code scales significantly better with a large
number of snapshots.
This is a very large commit and was written in a number of
pieces. But, they depend heavily on the disk format change and were
squashed together to make sure git bisect didn't end up in a
bad state wrt space balancing or the format change.
Signed-off-by: Yan Zheng <zheng.yan@oracle.com>
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2009-06-10 22:45:14 +08:00
|
|
|
return ERR_PTR(-ENOMEM);
|
2008-07-21 04:31:04 +08:00
|
|
|
|
|
|
|
if (inode->i_state & I_NEW) {
|
|
|
|
BTRFS_I(inode)->root = root;
|
|
|
|
memcpy(&BTRFS_I(inode)->location, location, sizeof(*location));
|
|
|
|
btrfs_read_locked_inode(inode);
|
Btrfs: Mixed back reference (FORWARD ROLLING FORMAT CHANGE)
This commit introduces a new kind of back reference for btrfs metadata.
Once a filesystem has been mounted with this commit, IT WILL NO LONGER
BE MOUNTABLE BY OLDER KERNELS.
When a tree block in subvolume tree is cow'd, the reference counts of all
extents it points to are increased by one. At transaction commit time,
the old root of the subvolume is recorded in a "dead root" data structure,
and the btree it points to is later walked, dropping reference counts
and freeing any blocks where the reference count goes to 0.
The increments done during cow and decrements done after commit cancel out,
and the walk is a very expensive way to go about freeing the blocks that
are no longer referenced by the new btree root. This commit reduces the
transaction overhead by avoiding the need for dead root records.
When a non-shared tree block is cow'd, we free the old block at once, and the
new block inherits old block's references. When a tree block with reference
count > 1 is cow'd, we increase the reference counts of all extents
the new block points to by one, and decrease the old block's reference count by
one.
This dead tree avoidance code removes the need to modify the reference
counts of lower level extents when a non-shared tree block is cow'd.
But we still need to update back ref for all pointers in the block.
This is because the location of the block is recorded in the back ref
item.
We can solve this by introducing a new type of back ref. The new
back ref provides information about pointer's key, level and in which
tree the pointer lives. This information allow us to find the pointer
by searching the tree. The shortcoming of the new back ref is that it
only works for pointers in tree blocks referenced by their owner trees.
This is mostly a problem for snapshots, where resolving one of these
fuzzy back references would be O(number_of_snapshots) and quite slow.
The solution used here is to use the fuzzy back references in the common
case where a given tree block is only referenced by one root,
and use the full back references when multiple roots have a reference
on a given block.
This commit adds per subvolume red-black tree to keep trace of cached
inodes. The red-black tree helps the balancing code to find cached
inodes whose inode numbers within a given range.
This commit improves the balancing code by introducing several data
structures to keep the state of balancing. The most important one
is the back ref cache. It caches how the upper level tree blocks are
referenced. This greatly reduce the overhead of checking back ref.
The improved balancing code scales significantly better with a large
number of snapshots.
This is a very large commit and was written in a number of
pieces. But, they depend heavily on the disk format change and were
squashed together to make sure git bisect didn't end up in a
bad state wrt space balancing or the format change.
Signed-off-by: Yan Zheng <zheng.yan@oracle.com>
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2009-06-10 22:45:14 +08:00
|
|
|
|
|
|
|
inode_tree_add(inode);
|
2008-07-21 04:31:04 +08:00
|
|
|
unlock_new_inode(inode);
|
|
|
|
}
|
|
|
|
|
|
|
|
return inode;
|
|
|
|
}
|
|
|
|
|
2009-09-22 03:56:00 +08:00
|
|
|
static struct inode *new_simple_dir(struct super_block *s,
|
|
|
|
struct btrfs_key *key,
|
|
|
|
struct btrfs_root *root)
|
|
|
|
{
|
|
|
|
struct inode *inode = new_inode(s);
|
|
|
|
|
|
|
|
if (!inode)
|
|
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
|
|
|
|
init_btrfs_i(inode);
|
|
|
|
|
|
|
|
BTRFS_I(inode)->root = root;
|
|
|
|
memcpy(&BTRFS_I(inode)->location, key, sizeof(*key));
|
|
|
|
BTRFS_I(inode)->dummy_inode = 1;
|
|
|
|
|
|
|
|
inode->i_ino = BTRFS_EMPTY_SUBVOL_DIR_OBJECTID;
|
|
|
|
inode->i_op = &simple_dir_inode_operations;
|
|
|
|
inode->i_fop = &simple_dir_operations;
|
|
|
|
inode->i_mode = S_IFDIR | S_IRUGO | S_IWUSR | S_IXUGO;
|
|
|
|
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
|
|
|
|
|
|
|
|
return inode;
|
|
|
|
}
|
|
|
|
|
2008-11-18 10:02:50 +08:00
|
|
|
struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry)
|
2007-06-12 18:35:45 +08:00
|
|
|
{
|
2009-01-06 10:25:51 +08:00
|
|
|
struct inode *inode;
|
2009-09-22 03:56:00 +08:00
|
|
|
struct btrfs_root *root = BTRFS_I(dir)->root;
|
2007-06-12 18:35:45 +08:00
|
|
|
struct btrfs_root *sub_root = root;
|
|
|
|
struct btrfs_key location;
|
2009-09-22 04:00:26 +08:00
|
|
|
int index;
|
Btrfs: Mixed back reference (FORWARD ROLLING FORMAT CHANGE)
This commit introduces a new kind of back reference for btrfs metadata.
Once a filesystem has been mounted with this commit, IT WILL NO LONGER
BE MOUNTABLE BY OLDER KERNELS.
When a tree block in subvolume tree is cow'd, the reference counts of all
extents it points to are increased by one. At transaction commit time,
the old root of the subvolume is recorded in a "dead root" data structure,
and the btree it points to is later walked, dropping reference counts
and freeing any blocks where the reference count goes to 0.
The increments done during cow and decrements done after commit cancel out,
and the walk is a very expensive way to go about freeing the blocks that
are no longer referenced by the new btree root. This commit reduces the
transaction overhead by avoiding the need for dead root records.
When a non-shared tree block is cow'd, we free the old block at once, and the
new block inherits old block's references. When a tree block with reference
count > 1 is cow'd, we increase the reference counts of all extents
the new block points to by one, and decrease the old block's reference count by
one.
This dead tree avoidance code removes the need to modify the reference
counts of lower level extents when a non-shared tree block is cow'd.
But we still need to update back ref for all pointers in the block.
This is because the location of the block is recorded in the back ref
item.
We can solve this by introducing a new type of back ref. The new
back ref provides information about pointer's key, level and in which
tree the pointer lives. This information allow us to find the pointer
by searching the tree. The shortcoming of the new back ref is that it
only works for pointers in tree blocks referenced by their owner trees.
This is mostly a problem for snapshots, where resolving one of these
fuzzy back references would be O(number_of_snapshots) and quite slow.
The solution used here is to use the fuzzy back references in the common
case where a given tree block is only referenced by one root,
and use the full back references when multiple roots have a reference
on a given block.
This commit adds per subvolume red-black tree to keep trace of cached
inodes. The red-black tree helps the balancing code to find cached
inodes whose inode numbers within a given range.
This commit improves the balancing code by introducing several data
structures to keep the state of balancing. The most important one
is the back ref cache. It caches how the upper level tree blocks are
referenced. This greatly reduce the overhead of checking back ref.
The improved balancing code scales significantly better with a large
number of snapshots.
This is a very large commit and was written in a number of
pieces. But, they depend heavily on the disk format change and were
squashed together to make sure git bisect didn't end up in a
bad state wrt space balancing or the format change.
Signed-off-by: Yan Zheng <zheng.yan@oracle.com>
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2009-06-10 22:45:14 +08:00
|
|
|
int ret;
|
2007-06-12 18:35:45 +08:00
|
|
|
|
2009-09-22 04:00:26 +08:00
|
|
|
dentry->d_op = &btrfs_dentry_operations;
|
|
|
|
|
2007-06-12 18:35:45 +08:00
|
|
|
if (dentry->d_name.len > BTRFS_NAME_LEN)
|
|
|
|
return ERR_PTR(-ENAMETOOLONG);
|
2007-10-16 04:14:19 +08:00
|
|
|
|
2007-06-12 18:35:45 +08:00
|
|
|
ret = btrfs_inode_by_name(dir, dentry, &location);
|
2007-10-16 04:14:19 +08:00
|
|
|
|
2007-06-12 18:35:45 +08:00
|
|
|
if (ret < 0)
|
|
|
|
return ERR_PTR(ret);
|
2007-10-16 04:14:19 +08:00
|
|
|
|
2009-09-22 03:56:00 +08:00
|
|
|
if (location.objectid == 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (location.type == BTRFS_INODE_ITEM_KEY) {
|
|
|
|
inode = btrfs_iget(dir->i_sb, &location, root);
|
|
|
|
return inode;
|
|
|
|
}
|
|
|
|
|
|
|
|
BUG_ON(location.type != BTRFS_ROOT_ITEM_KEY);
|
|
|
|
|
2009-09-22 04:00:26 +08:00
|
|
|
index = srcu_read_lock(&root->fs_info->subvol_srcu);
|
2009-09-22 03:56:00 +08:00
|
|
|
ret = fixup_tree_root_location(root, dir, dentry,
|
|
|
|
&location, &sub_root);
|
|
|
|
if (ret < 0) {
|
|
|
|
if (ret != -ENOENT)
|
|
|
|
inode = ERR_PTR(ret);
|
|
|
|
else
|
|
|
|
inode = new_simple_dir(dir->i_sb, &location, sub_root);
|
|
|
|
} else {
|
Btrfs: Mixed back reference (FORWARD ROLLING FORMAT CHANGE)
This commit introduces a new kind of back reference for btrfs metadata.
Once a filesystem has been mounted with this commit, IT WILL NO LONGER
BE MOUNTABLE BY OLDER KERNELS.
When a tree block in subvolume tree is cow'd, the reference counts of all
extents it points to are increased by one. At transaction commit time,
the old root of the subvolume is recorded in a "dead root" data structure,
and the btree it points to is later walked, dropping reference counts
and freeing any blocks where the reference count goes to 0.
The increments done during cow and decrements done after commit cancel out,
and the walk is a very expensive way to go about freeing the blocks that
are no longer referenced by the new btree root. This commit reduces the
transaction overhead by avoiding the need for dead root records.
When a non-shared tree block is cow'd, we free the old block at once, and the
new block inherits old block's references. When a tree block with reference
count > 1 is cow'd, we increase the reference counts of all extents
the new block points to by one, and decrease the old block's reference count by
one.
This dead tree avoidance code removes the need to modify the reference
counts of lower level extents when a non-shared tree block is cow'd.
But we still need to update back ref for all pointers in the block.
This is because the location of the block is recorded in the back ref
item.
We can solve this by introducing a new type of back ref. The new
back ref provides information about pointer's key, level and in which
tree the pointer lives. This information allow us to find the pointer
by searching the tree. The shortcoming of the new back ref is that it
only works for pointers in tree blocks referenced by their owner trees.
This is mostly a problem for snapshots, where resolving one of these
fuzzy back references would be O(number_of_snapshots) and quite slow.
The solution used here is to use the fuzzy back references in the common
case where a given tree block is only referenced by one root,
and use the full back references when multiple roots have a reference
on a given block.
This commit adds per subvolume red-black tree to keep trace of cached
inodes. The red-black tree helps the balancing code to find cached
inodes whose inode numbers within a given range.
This commit improves the balancing code by introducing several data
structures to keep the state of balancing. The most important one
is the back ref cache. It caches how the upper level tree blocks are
referenced. This greatly reduce the overhead of checking back ref.
The improved balancing code scales significantly better with a large
number of snapshots.
This is a very large commit and was written in a number of
pieces. But, they depend heavily on the disk format change and were
squashed together to make sure git bisect didn't end up in a
bad state wrt space balancing or the format change.
Signed-off-by: Yan Zheng <zheng.yan@oracle.com>
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2009-06-10 22:45:14 +08:00
|
|
|
inode = btrfs_iget(dir->i_sb, &location, sub_root);
|
2007-06-12 18:35:45 +08:00
|
|
|
}
|
2009-09-22 04:00:26 +08:00
|
|
|
srcu_read_unlock(&root->fs_info->subvol_srcu, index);
|
|
|
|
|
2008-11-18 10:02:50 +08:00
|
|
|
return inode;
|
|
|
|
}
|
|
|
|
|
2009-09-22 04:00:26 +08:00
|
|
|
static int btrfs_dentry_delete(struct dentry *dentry)
|
|
|
|
{
|
|
|
|
struct btrfs_root *root;
|
|
|
|
|
|
|
|
if (!dentry->d_inode)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
root = BTRFS_I(dentry->d_inode)->root;
|
|
|
|
if (btrfs_root_refs(&root->root_item) == 0)
|
|
|
|
return 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-11-18 10:02:50 +08:00
|
|
|
static struct dentry *btrfs_lookup(struct inode *dir, struct dentry *dentry,
|
|
|
|
struct nameidata *nd)
|
|
|
|
{
|
|
|
|
struct inode *inode;
|
|
|
|
|
|
|
|
inode = btrfs_lookup_dentry(dir, dentry);
|
|
|
|
if (IS_ERR(inode))
|
|
|
|
return ERR_CAST(inode);
|
2008-07-25 00:17:14 +08:00
|
|
|
|
2007-06-12 18:35:45 +08:00
|
|
|
return d_splice_alias(inode, dentry);
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned char btrfs_filetype_table[] = {
|
|
|
|
DT_UNKNOWN, DT_REG, DT_DIR, DT_CHR, DT_BLK, DT_FIFO, DT_SOCK, DT_LNK
|
|
|
|
};
|
|
|
|
|
2008-08-07 02:42:33 +08:00
|
|
|
static int btrfs_real_readdir(struct file *filp, void *dirent,
|
|
|
|
filldir_t filldir)
|
2007-06-12 18:35:45 +08:00
|
|
|
{
|
2007-12-19 05:15:09 +08:00
|
|
|
struct inode *inode = filp->f_dentry->d_inode;
|
2007-06-12 18:35:45 +08:00
|
|
|
struct btrfs_root *root = BTRFS_I(inode)->root;
|
|
|
|
struct btrfs_item *item;
|
|
|
|
struct btrfs_dir_item *di;
|
|
|
|
struct btrfs_key key;
|
2007-10-16 04:14:19 +08:00
|
|
|
struct btrfs_key found_key;
|
2007-06-12 18:35:45 +08:00
|
|
|
struct btrfs_path *path;
|
|
|
|
int ret;
|
|
|
|
u32 nritems;
|
2007-10-16 04:14:19 +08:00
|
|
|
struct extent_buffer *leaf;
|
2007-06-12 18:35:45 +08:00
|
|
|
int slot;
|
|
|
|
int advance;
|
|
|
|
unsigned char d_type;
|
|
|
|
int over = 0;
|
|
|
|
u32 di_cur;
|
|
|
|
u32 di_total;
|
|
|
|
u32 di_len;
|
|
|
|
int key_type = BTRFS_DIR_INDEX_KEY;
|
2007-10-16 04:14:19 +08:00
|
|
|
char tmp_name[32];
|
|
|
|
char *name_ptr;
|
|
|
|
int name_len;
|
2007-06-12 18:35:45 +08:00
|
|
|
|
|
|
|
/* FIXME, use a real flag for deciding about the key type */
|
|
|
|
if (root->fs_info->tree_root == root)
|
|
|
|
key_type = BTRFS_DIR_ITEM_KEY;
|
2007-10-16 04:14:19 +08:00
|
|
|
|
2007-12-13 03:38:19 +08:00
|
|
|
/* special case for "." */
|
|
|
|
if (filp->f_pos == 0) {
|
|
|
|
over = filldir(dirent, ".", 1,
|
|
|
|
1, inode->i_ino,
|
|
|
|
DT_DIR);
|
|
|
|
if (over)
|
|
|
|
return 0;
|
|
|
|
filp->f_pos = 1;
|
|
|
|
}
|
|
|
|
/* special case for .., just use the back ref */
|
|
|
|
if (filp->f_pos == 1) {
|
2008-08-17 22:14:48 +08:00
|
|
|
u64 pino = parent_ino(filp->f_path.dentry);
|
2007-12-13 03:38:19 +08:00
|
|
|
over = filldir(dirent, "..", 2,
|
2008-08-17 22:14:48 +08:00
|
|
|
2, pino, DT_DIR);
|
2007-12-13 03:38:19 +08:00
|
|
|
if (over)
|
2008-08-18 00:08:36 +08:00
|
|
|
return 0;
|
2007-12-13 03:38:19 +08:00
|
|
|
filp->f_pos = 2;
|
|
|
|
}
|
2008-08-18 00:08:36 +08:00
|
|
|
path = btrfs_alloc_path();
|
|
|
|
path->reada = 2;
|
|
|
|
|
2007-06-12 18:35:45 +08:00
|
|
|
btrfs_set_key_type(&key, key_type);
|
|
|
|
key.offset = filp->f_pos;
|
2008-08-18 00:08:36 +08:00
|
|
|
key.objectid = inode->i_ino;
|
2007-10-16 04:14:19 +08:00
|
|
|
|
2007-06-12 18:35:45 +08:00
|
|
|
ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
|
|
|
|
if (ret < 0)
|
|
|
|
goto err;
|
|
|
|
advance = 0;
|
2008-08-18 00:08:36 +08:00
|
|
|
|
|
|
|
while (1) {
|
2007-10-16 04:14:19 +08:00
|
|
|
leaf = path->nodes[0];
|
|
|
|
nritems = btrfs_header_nritems(leaf);
|
2007-06-12 18:35:45 +08:00
|
|
|
slot = path->slots[0];
|
|
|
|
if (advance || slot >= nritems) {
|
2008-08-18 00:08:36 +08:00
|
|
|
if (slot >= nritems - 1) {
|
2007-06-12 18:35:45 +08:00
|
|
|
ret = btrfs_next_leaf(root, path);
|
|
|
|
if (ret)
|
|
|
|
break;
|
2007-10-16 04:14:19 +08:00
|
|
|
leaf = path->nodes[0];
|
|
|
|
nritems = btrfs_header_nritems(leaf);
|
2007-06-12 18:35:45 +08:00
|
|
|
slot = path->slots[0];
|
|
|
|
} else {
|
|
|
|
slot++;
|
|
|
|
path->slots[0]++;
|
|
|
|
}
|
|
|
|
}
|
2008-11-18 10:02:50 +08:00
|
|
|
|
2007-06-12 18:35:45 +08:00
|
|
|
advance = 1;
|
2007-10-16 04:14:19 +08:00
|
|
|
item = btrfs_item_nr(leaf, slot);
|
|
|
|
btrfs_item_key_to_cpu(leaf, &found_key, slot);
|
|
|
|
|
|
|
|
if (found_key.objectid != key.objectid)
|
2007-06-12 18:35:45 +08:00
|
|
|
break;
|
2007-10-16 04:14:19 +08:00
|
|
|
if (btrfs_key_type(&found_key) != key_type)
|
2007-06-12 18:35:45 +08:00
|
|
|
break;
|
2007-10-16 04:14:19 +08:00
|
|
|
if (found_key.offset < filp->f_pos)
|
2007-06-12 18:35:45 +08:00
|
|
|
continue;
|
2007-10-16 04:14:19 +08:00
|
|
|
|
|
|
|
filp->f_pos = found_key.offset;
|
2008-08-18 00:08:36 +08:00
|
|
|
|
2007-06-12 18:35:45 +08:00
|
|
|
di = btrfs_item_ptr(leaf, slot, struct btrfs_dir_item);
|
|
|
|
di_cur = 0;
|
2007-10-16 04:14:19 +08:00
|
|
|
di_total = btrfs_item_size(leaf, item);
|
2008-08-18 00:08:36 +08:00
|
|
|
|
|
|
|
while (di_cur < di_total) {
|
2007-10-16 04:14:19 +08:00
|
|
|
struct btrfs_key location;
|
|
|
|
|
|
|
|
name_len = btrfs_dir_name_len(leaf, di);
|
2008-08-18 00:08:36 +08:00
|
|
|
if (name_len <= sizeof(tmp_name)) {
|
2007-10-16 04:14:19 +08:00
|
|
|
name_ptr = tmp_name;
|
|
|
|
} else {
|
|
|
|
name_ptr = kmalloc(name_len, GFP_NOFS);
|
2008-08-18 00:08:36 +08:00
|
|
|
if (!name_ptr) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto err;
|
|
|
|
}
|
2007-10-16 04:14:19 +08:00
|
|
|
}
|
|
|
|
read_extent_buffer(leaf, name_ptr,
|
|
|
|
(unsigned long)(di + 1), name_len);
|
|
|
|
|
|
|
|
d_type = btrfs_filetype_table[btrfs_dir_type(leaf, di)];
|
|
|
|
btrfs_dir_item_key_to_cpu(leaf, di, &location);
|
2008-11-18 10:02:50 +08:00
|
|
|
|
|
|
|
/* is this a reference to our own snapshot? If so
|
|
|
|
* skip it
|
|
|
|
*/
|
|
|
|
if (location.type == BTRFS_ROOT_ITEM_KEY &&
|
|
|
|
location.objectid == root->root_key.objectid) {
|
|
|
|
over = 0;
|
|
|
|
goto skip;
|
|
|
|
}
|
2007-10-16 04:14:19 +08:00
|
|
|
over = filldir(dirent, name_ptr, name_len,
|
2008-08-18 00:08:36 +08:00
|
|
|
found_key.offset, location.objectid,
|
2007-06-12 18:35:45 +08:00
|
|
|
d_type);
|
2007-10-16 04:14:19 +08:00
|
|
|
|
2008-11-18 10:02:50 +08:00
|
|
|
skip:
|
2007-10-16 04:14:19 +08:00
|
|
|
if (name_ptr != tmp_name)
|
|
|
|
kfree(name_ptr);
|
|
|
|
|
2007-06-12 18:35:45 +08:00
|
|
|
if (over)
|
|
|
|
goto nopos;
|
2007-11-17 00:45:54 +08:00
|
|
|
di_len = btrfs_dir_name_len(leaf, di) +
|
2008-08-18 00:08:36 +08:00
|
|
|
btrfs_dir_data_len(leaf, di) + sizeof(*di);
|
2007-06-12 18:35:45 +08:00
|
|
|
di_cur += di_len;
|
|
|
|
di = (struct btrfs_dir_item *)((char *)di + di_len);
|
|
|
|
}
|
|
|
|
}
|
2008-08-18 00:08:36 +08:00
|
|
|
|
|
|
|
/* Reached end of directory/root. Bump pos past the last item. */
|
2008-02-20 00:41:02 +08:00
|
|
|
if (key_type == BTRFS_DIR_INDEX_KEY)
|
2009-01-29 04:34:27 +08:00
|
|
|
filp->f_pos = INT_LIMIT(off_t);
|
2008-02-20 00:41:02 +08:00
|
|
|
else
|
|
|
|
filp->f_pos++;
|
2007-06-12 18:35:45 +08:00
|
|
|
nopos:
|
|
|
|
ret = 0;
|
|
|
|
err:
|
|
|
|
btrfs_free_path(path);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int btrfs_write_inode(struct inode *inode, int wait)
|
|
|
|
{
|
|
|
|
struct btrfs_root *root = BTRFS_I(inode)->root;
|
|
|
|
struct btrfs_trans_handle *trans;
|
|
|
|
int ret = 0;
|
|
|
|
|
2008-11-13 03:34:12 +08:00
|
|
|
if (root->fs_info->btree_inode == inode)
|
2008-08-06 01:30:48 +08:00
|
|
|
return 0;
|
|
|
|
|
2007-06-12 18:35:45 +08:00
|
|
|
if (wait) {
|
2008-07-18 00:54:14 +08:00
|
|
|
trans = btrfs_join_transaction(root, 1);
|
2007-06-12 18:35:45 +08:00
|
|
|
btrfs_set_trans_block_group(trans, inode);
|
|
|
|
ret = btrfs_commit_transaction(trans, root);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2007-06-23 02:16:25 +08:00
|
|
|
* This is somewhat expensive, updating the tree every time the
|
2007-06-12 18:35:45 +08:00
|
|
|
* inode changes. But, it is most likely to find the inode in cache.
|
|
|
|
* FIXME, needs more benchmarking...there are no reasons other than performance
|
|
|
|
* to keep or drop this code.
|
|
|
|
*/
|
|
|
|
void btrfs_dirty_inode(struct inode *inode)
|
|
|
|
{
|
|
|
|
struct btrfs_root *root = BTRFS_I(inode)->root;
|
|
|
|
struct btrfs_trans_handle *trans;
|
|
|
|
|
2008-07-18 00:54:14 +08:00
|
|
|
trans = btrfs_join_transaction(root, 1);
|
2007-06-12 18:35:45 +08:00
|
|
|
btrfs_set_trans_block_group(trans, inode);
|
|
|
|
btrfs_update_inode(trans, root, inode);
|
|
|
|
btrfs_end_transaction(trans, root);
|
|
|
|
}
|
|
|
|
|
2008-09-30 03:18:18 +08:00
|
|
|
/*
|
|
|
|
* find the highest existing sequence number in a directory
|
|
|
|
* and then set the in-memory index_cnt variable to reflect
|
|
|
|
* free sequence numbers
|
|
|
|
*/
|
2008-07-25 00:12:38 +08:00
|
|
|
static int btrfs_set_inode_index_count(struct inode *inode)
|
|
|
|
{
|
|
|
|
struct btrfs_root *root = BTRFS_I(inode)->root;
|
|
|
|
struct btrfs_key key, found_key;
|
|
|
|
struct btrfs_path *path;
|
|
|
|
struct extent_buffer *leaf;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
key.objectid = inode->i_ino;
|
|
|
|
btrfs_set_key_type(&key, BTRFS_DIR_INDEX_KEY);
|
|
|
|
key.offset = (u64)-1;
|
|
|
|
|
|
|
|
path = btrfs_alloc_path();
|
|
|
|
if (!path)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
/* FIXME: we should be able to handle this */
|
|
|
|
if (ret == 0)
|
|
|
|
goto out;
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* MAGIC NUMBER EXPLANATION:
|
|
|
|
* since we search a directory based on f_pos we have to start at 2
|
|
|
|
* since '.' and '..' have f_pos of 0 and 1 respectively, so everybody
|
|
|
|
* else has to start at 2
|
|
|
|
*/
|
|
|
|
if (path->slots[0] == 0) {
|
|
|
|
BTRFS_I(inode)->index_cnt = 2;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
path->slots[0]--;
|
|
|
|
|
|
|
|
leaf = path->nodes[0];
|
|
|
|
btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
|
|
|
|
|
|
|
|
if (found_key.objectid != inode->i_ino ||
|
|
|
|
btrfs_key_type(&found_key) != BTRFS_DIR_INDEX_KEY) {
|
|
|
|
BTRFS_I(inode)->index_cnt = 2;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
BTRFS_I(inode)->index_cnt = found_key.offset + 1;
|
|
|
|
out:
|
|
|
|
btrfs_free_path(path);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2008-09-30 03:18:18 +08:00
|
|
|
/*
|
|
|
|
* helper to find a free sequence number in a given directory. This current
|
|
|
|
* code is very simple, later versions will do smarter things in the btree
|
|
|
|
*/
|
2008-11-18 10:02:50 +08:00
|
|
|
int btrfs_set_inode_index(struct inode *dir, u64 *index)
|
2008-07-25 00:12:38 +08:00
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
if (BTRFS_I(dir)->index_cnt == (u64)-1) {
|
|
|
|
ret = btrfs_set_inode_index_count(dir);
|
2009-01-06 10:25:51 +08:00
|
|
|
if (ret)
|
2008-07-25 00:12:38 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2008-08-05 23:18:09 +08:00
|
|
|
*index = BTRFS_I(dir)->index_cnt;
|
2008-07-25 00:12:38 +08:00
|
|
|
BTRFS_I(dir)->index_cnt++;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2007-06-12 18:35:45 +08:00
|
|
|
static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,
|
|
|
|
struct btrfs_root *root,
|
2008-07-25 00:12:38 +08:00
|
|
|
struct inode *dir,
|
2008-01-30 04:15:18 +08:00
|
|
|
const char *name, int name_len,
|
2008-12-12 05:30:39 +08:00
|
|
|
u64 ref_objectid, u64 objectid,
|
|
|
|
u64 alloc_hint, int mode, u64 *index)
|
2007-06-12 18:35:45 +08:00
|
|
|
{
|
|
|
|
struct inode *inode;
|
2007-10-16 04:14:19 +08:00
|
|
|
struct btrfs_inode_item *inode_item;
|
2007-06-12 18:35:45 +08:00
|
|
|
struct btrfs_key *location;
|
2007-10-16 04:14:19 +08:00
|
|
|
struct btrfs_path *path;
|
2008-01-30 04:15:18 +08:00
|
|
|
struct btrfs_inode_ref *ref;
|
|
|
|
struct btrfs_key key[2];
|
|
|
|
u32 sizes[2];
|
|
|
|
unsigned long ptr;
|
2007-06-12 18:35:45 +08:00
|
|
|
int ret;
|
|
|
|
int owner;
|
|
|
|
|
2007-10-16 04:14:19 +08:00
|
|
|
path = btrfs_alloc_path();
|
|
|
|
BUG_ON(!path);
|
|
|
|
|
2007-06-12 18:35:45 +08:00
|
|
|
inode = new_inode(root->fs_info->sb);
|
|
|
|
if (!inode)
|
|
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
|
2008-07-25 00:12:38 +08:00
|
|
|
if (dir) {
|
2008-11-18 10:02:50 +08:00
|
|
|
ret = btrfs_set_inode_index(dir, index);
|
2009-04-03 04:46:06 +08:00
|
|
|
if (ret) {
|
|
|
|
iput(inode);
|
2008-07-25 00:12:38 +08:00
|
|
|
return ERR_PTR(ret);
|
2009-04-03 04:46:06 +08:00
|
|
|
}
|
2008-07-25 00:12:38 +08:00
|
|
|
}
|
|
|
|
/*
|
|
|
|
* index_cnt is ignored for everything but a dir,
|
|
|
|
* btrfs_get_inode_index_count has an explanation for the magic
|
|
|
|
* number
|
|
|
|
*/
|
2008-09-06 04:13:11 +08:00
|
|
|
init_btrfs_i(inode);
|
2008-07-25 00:12:38 +08:00
|
|
|
BTRFS_I(inode)->index_cnt = 2;
|
2007-06-12 18:35:45 +08:00
|
|
|
BTRFS_I(inode)->root = root;
|
2008-09-06 04:13:11 +08:00
|
|
|
BTRFS_I(inode)->generation = trans->transid;
|
2009-02-21 00:00:09 +08:00
|
|
|
btrfs_set_inode_space_info(root, inode);
|
2007-08-28 04:49:44 +08:00
|
|
|
|
2007-06-12 18:35:45 +08:00
|
|
|
if (mode & S_IFDIR)
|
|
|
|
owner = 0;
|
|
|
|
else
|
|
|
|
owner = 1;
|
2008-12-12 05:30:39 +08:00
|
|
|
BTRFS_I(inode)->block_group =
|
|
|
|
btrfs_find_block_group(root, 0, alloc_hint, owner);
|
2008-01-30 04:15:18 +08:00
|
|
|
|
|
|
|
key[0].objectid = objectid;
|
|
|
|
btrfs_set_key_type(&key[0], BTRFS_INODE_ITEM_KEY);
|
|
|
|
key[0].offset = 0;
|
|
|
|
|
|
|
|
key[1].objectid = objectid;
|
|
|
|
btrfs_set_key_type(&key[1], BTRFS_INODE_REF_KEY);
|
|
|
|
key[1].offset = ref_objectid;
|
|
|
|
|
|
|
|
sizes[0] = sizeof(struct btrfs_inode_item);
|
|
|
|
sizes[1] = name_len + sizeof(*ref);
|
|
|
|
|
2009-03-13 23:00:37 +08:00
|
|
|
path->leave_spinning = 1;
|
2008-01-30 04:15:18 +08:00
|
|
|
ret = btrfs_insert_empty_items(trans, root, path, key, sizes, 2);
|
|
|
|
if (ret != 0)
|
2007-10-16 04:14:19 +08:00
|
|
|
goto fail;
|
|
|
|
|
2008-11-20 11:00:53 +08:00
|
|
|
inode->i_uid = current_fsuid();
|
2009-02-04 22:29:54 +08:00
|
|
|
|
2009-02-07 00:35:57 +08:00
|
|
|
if (dir && (dir->i_mode & S_ISGID)) {
|
2009-02-04 22:29:54 +08:00
|
|
|
inode->i_gid = dir->i_gid;
|
|
|
|
if (S_ISDIR(mode))
|
|
|
|
mode |= S_ISGID;
|
|
|
|
} else
|
|
|
|
inode->i_gid = current_fsgid();
|
|
|
|
|
2007-06-12 18:35:45 +08:00
|
|
|
inode->i_mode = mode;
|
|
|
|
inode->i_ino = objectid;
|
2008-10-09 23:46:29 +08:00
|
|
|
inode_set_bytes(inode, 0);
|
2007-06-12 18:35:45 +08:00
|
|
|
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
|
2007-10-16 04:14:19 +08:00
|
|
|
inode_item = btrfs_item_ptr(path->nodes[0], path->slots[0],
|
|
|
|
struct btrfs_inode_item);
|
2008-09-06 04:13:11 +08:00
|
|
|
fill_inode_item(trans, path->nodes[0], inode_item, inode);
|
2008-01-30 04:15:18 +08:00
|
|
|
|
|
|
|
ref = btrfs_item_ptr(path->nodes[0], path->slots[0] + 1,
|
|
|
|
struct btrfs_inode_ref);
|
|
|
|
btrfs_set_inode_ref_name_len(path->nodes[0], ref, name_len);
|
2008-08-05 23:18:09 +08:00
|
|
|
btrfs_set_inode_ref_index(path->nodes[0], ref, *index);
|
2008-01-30 04:15:18 +08:00
|
|
|
ptr = (unsigned long)(ref + 1);
|
|
|
|
write_extent_buffer(path->nodes[0], name, ptr, name_len);
|
|
|
|
|
2007-10-16 04:14:19 +08:00
|
|
|
btrfs_mark_buffer_dirty(path->nodes[0]);
|
|
|
|
btrfs_free_path(path);
|
|
|
|
|
2007-06-12 18:35:45 +08:00
|
|
|
location = &BTRFS_I(inode)->location;
|
|
|
|
location->objectid = objectid;
|
|
|
|
location->offset = 0;
|
|
|
|
btrfs_set_key_type(location, BTRFS_INODE_ITEM_KEY);
|
|
|
|
|
2009-04-17 16:37:41 +08:00
|
|
|
btrfs_inherit_iflags(inode, dir);
|
|
|
|
|
2009-07-03 00:26:06 +08:00
|
|
|
if ((mode & S_IFREG)) {
|
|
|
|
if (btrfs_test_opt(root, NODATASUM))
|
|
|
|
BTRFS_I(inode)->flags |= BTRFS_INODE_NODATASUM;
|
|
|
|
if (btrfs_test_opt(root, NODATACOW))
|
|
|
|
BTRFS_I(inode)->flags |= BTRFS_INODE_NODATACOW;
|
|
|
|
}
|
|
|
|
|
2007-06-12 18:35:45 +08:00
|
|
|
insert_inode_hash(inode);
|
Btrfs: Mixed back reference (FORWARD ROLLING FORMAT CHANGE)
This commit introduces a new kind of back reference for btrfs metadata.
Once a filesystem has been mounted with this commit, IT WILL NO LONGER
BE MOUNTABLE BY OLDER KERNELS.
When a tree block in subvolume tree is cow'd, the reference counts of all
extents it points to are increased by one. At transaction commit time,
the old root of the subvolume is recorded in a "dead root" data structure,
and the btree it points to is later walked, dropping reference counts
and freeing any blocks where the reference count goes to 0.
The increments done during cow and decrements done after commit cancel out,
and the walk is a very expensive way to go about freeing the blocks that
are no longer referenced by the new btree root. This commit reduces the
transaction overhead by avoiding the need for dead root records.
When a non-shared tree block is cow'd, we free the old block at once, and the
new block inherits old block's references. When a tree block with reference
count > 1 is cow'd, we increase the reference counts of all extents
the new block points to by one, and decrease the old block's reference count by
one.
This dead tree avoidance code removes the need to modify the reference
counts of lower level extents when a non-shared tree block is cow'd.
But we still need to update back ref for all pointers in the block.
This is because the location of the block is recorded in the back ref
item.
We can solve this by introducing a new type of back ref. The new
back ref provides information about pointer's key, level and in which
tree the pointer lives. This information allow us to find the pointer
by searching the tree. The shortcoming of the new back ref is that it
only works for pointers in tree blocks referenced by their owner trees.
This is mostly a problem for snapshots, where resolving one of these
fuzzy back references would be O(number_of_snapshots) and quite slow.
The solution used here is to use the fuzzy back references in the common
case where a given tree block is only referenced by one root,
and use the full back references when multiple roots have a reference
on a given block.
This commit adds per subvolume red-black tree to keep trace of cached
inodes. The red-black tree helps the balancing code to find cached
inodes whose inode numbers within a given range.
This commit improves the balancing code by introducing several data
structures to keep the state of balancing. The most important one
is the back ref cache. It caches how the upper level tree blocks are
referenced. This greatly reduce the overhead of checking back ref.
The improved balancing code scales significantly better with a large
number of snapshots.
This is a very large commit and was written in a number of
pieces. But, they depend heavily on the disk format change and were
squashed together to make sure git bisect didn't end up in a
bad state wrt space balancing or the format change.
Signed-off-by: Yan Zheng <zheng.yan@oracle.com>
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2009-06-10 22:45:14 +08:00
|
|
|
inode_tree_add(inode);
|
2007-06-12 18:35:45 +08:00
|
|
|
return inode;
|
2007-10-16 04:14:19 +08:00
|
|
|
fail:
|
2008-07-25 00:12:38 +08:00
|
|
|
if (dir)
|
|
|
|
BTRFS_I(dir)->index_cnt--;
|
2007-10-16 04:14:19 +08:00
|
|
|
btrfs_free_path(path);
|
2009-04-03 04:46:06 +08:00
|
|
|
iput(inode);
|
2007-10-16 04:14:19 +08:00
|
|
|
return ERR_PTR(ret);
|
2007-06-12 18:35:45 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline u8 btrfs_inode_type(struct inode *inode)
|
|
|
|
{
|
|
|
|
return btrfs_type_by_mode[(inode->i_mode & S_IFMT) >> S_SHIFT];
|
|
|
|
}
|
|
|
|
|
2008-09-30 03:18:18 +08:00
|
|
|
/*
|
|
|
|
* utility function to add 'inode' into 'parent_inode' with
|
|
|
|
* a give name and a given sequence number.
|
|
|
|
* if 'add_backref' is true, also insert a backref from the
|
|
|
|
* inode to the parent directory.
|
|
|
|
*/
|
2008-09-06 04:13:11 +08:00
|
|
|
int btrfs_add_link(struct btrfs_trans_handle *trans,
|
|
|
|
struct inode *parent_inode, struct inode *inode,
|
|
|
|
const char *name, int name_len, int add_backref, u64 index)
|
2007-06-12 18:35:45 +08:00
|
|
|
{
|
2009-09-22 03:56:00 +08:00
|
|
|
int ret = 0;
|
2007-06-12 18:35:45 +08:00
|
|
|
struct btrfs_key key;
|
2008-09-06 04:13:11 +08:00
|
|
|
struct btrfs_root *root = BTRFS_I(parent_inode)->root;
|
2007-10-16 04:14:19 +08:00
|
|
|
|
2009-09-22 03:56:00 +08:00
|
|
|
if (unlikely(inode->i_ino == BTRFS_FIRST_FREE_OBJECTID)) {
|
|
|
|
memcpy(&key, &BTRFS_I(inode)->root->root_key, sizeof(key));
|
|
|
|
} else {
|
|
|
|
key.objectid = inode->i_ino;
|
|
|
|
btrfs_set_key_type(&key, BTRFS_INODE_ITEM_KEY);
|
|
|
|
key.offset = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (unlikely(inode->i_ino == BTRFS_FIRST_FREE_OBJECTID)) {
|
|
|
|
ret = btrfs_add_root_ref(trans, root->fs_info->tree_root,
|
|
|
|
key.objectid, root->root_key.objectid,
|
|
|
|
parent_inode->i_ino,
|
|
|
|
index, name, name_len);
|
|
|
|
} else if (add_backref) {
|
|
|
|
ret = btrfs_insert_inode_ref(trans, root,
|
|
|
|
name, name_len, inode->i_ino,
|
|
|
|
parent_inode->i_ino, index);
|
|
|
|
}
|
2007-06-12 18:35:45 +08:00
|
|
|
|
|
|
|
if (ret == 0) {
|
2009-09-22 03:56:00 +08:00
|
|
|
ret = btrfs_insert_dir_item(trans, root, name, name_len,
|
|
|
|
parent_inode->i_ino, &key,
|
|
|
|
btrfs_inode_type(inode), index);
|
|
|
|
BUG_ON(ret);
|
|
|
|
|
2008-07-18 00:54:05 +08:00
|
|
|
btrfs_i_size_write(parent_inode, parent_inode->i_size +
|
2008-09-06 04:13:11 +08:00
|
|
|
name_len * 2);
|
2007-06-25 22:09:33 +08:00
|
|
|
parent_inode->i_mtime = parent_inode->i_ctime = CURRENT_TIME;
|
2008-09-06 04:13:11 +08:00
|
|
|
ret = btrfs_update_inode(trans, root, parent_inode);
|
2007-06-12 18:35:45 +08:00
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int btrfs_add_nondir(struct btrfs_trans_handle *trans,
|
2008-01-30 04:15:18 +08:00
|
|
|
struct dentry *dentry, struct inode *inode,
|
2008-08-05 23:18:09 +08:00
|
|
|
int backref, u64 index)
|
2007-06-12 18:35:45 +08:00
|
|
|
{
|
2008-09-06 04:13:11 +08:00
|
|
|
int err = btrfs_add_link(trans, dentry->d_parent->d_inode,
|
|
|
|
inode, dentry->d_name.name,
|
|
|
|
dentry->d_name.len, backref, index);
|
2007-06-12 18:35:45 +08:00
|
|
|
if (!err) {
|
|
|
|
d_instantiate(dentry, inode);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (err > 0)
|
|
|
|
err = -EEXIST;
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2007-07-11 22:18:17 +08:00
|
|
|
static int btrfs_mknod(struct inode *dir, struct dentry *dentry,
|
|
|
|
int mode, dev_t rdev)
|
|
|
|
{
|
|
|
|
struct btrfs_trans_handle *trans;
|
|
|
|
struct btrfs_root *root = BTRFS_I(dir)->root;
|
2007-12-22 05:27:21 +08:00
|
|
|
struct inode *inode = NULL;
|
2007-07-11 22:18:17 +08:00
|
|
|
int err;
|
|
|
|
int drop_inode = 0;
|
|
|
|
u64 objectid;
|
2007-12-22 05:27:21 +08:00
|
|
|
unsigned long nr = 0;
|
2008-08-05 23:18:09 +08:00
|
|
|
u64 index = 0;
|
2007-07-11 22:18:17 +08:00
|
|
|
|
|
|
|
if (!new_valid_dev(rdev))
|
|
|
|
return -EINVAL;
|
|
|
|
|
2009-09-12 04:12:44 +08:00
|
|
|
/*
|
|
|
|
* 2 for inode item and ref
|
|
|
|
* 2 for dir items
|
|
|
|
* 1 for xattr if selinux is on
|
|
|
|
*/
|
|
|
|
err = btrfs_reserve_metadata_space(root, 5);
|
2007-12-22 05:27:21 +08:00
|
|
|
if (err)
|
2009-09-12 04:12:44 +08:00
|
|
|
return err;
|
2007-12-22 05:27:21 +08:00
|
|
|
|
2007-07-11 22:18:17 +08:00
|
|
|
trans = btrfs_start_transaction(root, 1);
|
2009-09-12 04:12:44 +08:00
|
|
|
if (!trans)
|
|
|
|
goto fail;
|
2007-07-11 22:18:17 +08:00
|
|
|
btrfs_set_trans_block_group(trans, dir);
|
|
|
|
|
|
|
|
err = btrfs_find_free_objectid(trans, root, dir->i_ino, &objectid);
|
|
|
|
if (err) {
|
|
|
|
err = -ENOSPC;
|
|
|
|
goto out_unlock;
|
|
|
|
}
|
|
|
|
|
2008-07-25 00:12:38 +08:00
|
|
|
inode = btrfs_new_inode(trans, root, dir, dentry->d_name.name,
|
2008-01-30 04:15:18 +08:00
|
|
|
dentry->d_name.len,
|
|
|
|
dentry->d_parent->d_inode->i_ino, objectid,
|
2008-08-05 23:18:09 +08:00
|
|
|
BTRFS_I(dir)->block_group, mode, &index);
|
2007-07-11 22:18:17 +08:00
|
|
|
err = PTR_ERR(inode);
|
|
|
|
if (IS_ERR(inode))
|
|
|
|
goto out_unlock;
|
|
|
|
|
2009-02-04 22:29:13 +08:00
|
|
|
err = btrfs_init_inode_security(inode, dir);
|
2008-07-25 00:16:36 +08:00
|
|
|
if (err) {
|
|
|
|
drop_inode = 1;
|
|
|
|
goto out_unlock;
|
|
|
|
}
|
|
|
|
|
2007-07-11 22:18:17 +08:00
|
|
|
btrfs_set_trans_block_group(trans, inode);
|
2008-08-05 23:18:09 +08:00
|
|
|
err = btrfs_add_nondir(trans, dentry, inode, 0, index);
|
2007-07-11 22:18:17 +08:00
|
|
|
if (err)
|
|
|
|
drop_inode = 1;
|
|
|
|
else {
|
|
|
|
inode->i_op = &btrfs_special_inode_operations;
|
|
|
|
init_special_inode(inode, inode->i_mode, rdev);
|
2007-08-29 21:11:44 +08:00
|
|
|
btrfs_update_inode(trans, root, inode);
|
2007-07-11 22:18:17 +08:00
|
|
|
}
|
|
|
|
btrfs_update_inode_block_group(trans, inode);
|
|
|
|
btrfs_update_inode_block_group(trans, dir);
|
|
|
|
out_unlock:
|
2007-09-17 22:58:06 +08:00
|
|
|
nr = trans->blocks_used;
|
2008-06-26 04:01:31 +08:00
|
|
|
btrfs_end_transaction_throttle(trans, root);
|
2007-12-22 05:27:21 +08:00
|
|
|
fail:
|
2009-09-12 04:12:44 +08:00
|
|
|
btrfs_unreserve_metadata_space(root, 5);
|
2007-07-11 22:18:17 +08:00
|
|
|
if (drop_inode) {
|
|
|
|
inode_dec_link_count(inode);
|
|
|
|
iput(inode);
|
|
|
|
}
|
2007-09-17 22:58:06 +08:00
|
|
|
btrfs_btree_balance_dirty(root, nr);
|
2007-07-11 22:18:17 +08:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2007-06-12 18:35:45 +08:00
|
|
|
static int btrfs_create(struct inode *dir, struct dentry *dentry,
|
|
|
|
int mode, struct nameidata *nd)
|
|
|
|
{
|
|
|
|
struct btrfs_trans_handle *trans;
|
|
|
|
struct btrfs_root *root = BTRFS_I(dir)->root;
|
2007-12-22 05:27:21 +08:00
|
|
|
struct inode *inode = NULL;
|
2007-06-12 18:35:45 +08:00
|
|
|
int err;
|
|
|
|
int drop_inode = 0;
|
2007-12-22 05:27:21 +08:00
|
|
|
unsigned long nr = 0;
|
2007-06-12 18:35:45 +08:00
|
|
|
u64 objectid;
|
2008-08-05 23:18:09 +08:00
|
|
|
u64 index = 0;
|
2007-06-12 18:35:45 +08:00
|
|
|
|
2009-09-12 04:12:44 +08:00
|
|
|
/*
|
|
|
|
* 2 for inode item and ref
|
|
|
|
* 2 for dir items
|
|
|
|
* 1 for xattr if selinux is on
|
|
|
|
*/
|
|
|
|
err = btrfs_reserve_metadata_space(root, 5);
|
2007-12-22 05:27:21 +08:00
|
|
|
if (err)
|
2009-09-12 04:12:44 +08:00
|
|
|
return err;
|
|
|
|
|
2007-06-12 18:35:45 +08:00
|
|
|
trans = btrfs_start_transaction(root, 1);
|
2009-09-12 04:12:44 +08:00
|
|
|
if (!trans)
|
|
|
|
goto fail;
|
2007-06-12 18:35:45 +08:00
|
|
|
btrfs_set_trans_block_group(trans, dir);
|
|
|
|
|
|
|
|
err = btrfs_find_free_objectid(trans, root, dir->i_ino, &objectid);
|
|
|
|
if (err) {
|
|
|
|
err = -ENOSPC;
|
|
|
|
goto out_unlock;
|
|
|
|
}
|
|
|
|
|
2008-07-25 00:12:38 +08:00
|
|
|
inode = btrfs_new_inode(trans, root, dir, dentry->d_name.name,
|
2008-01-30 04:15:18 +08:00
|
|
|
dentry->d_name.len,
|
|
|
|
dentry->d_parent->d_inode->i_ino,
|
2008-08-05 23:18:09 +08:00
|
|
|
objectid, BTRFS_I(dir)->block_group, mode,
|
|
|
|
&index);
|
2007-06-12 18:35:45 +08:00
|
|
|
err = PTR_ERR(inode);
|
|
|
|
if (IS_ERR(inode))
|
|
|
|
goto out_unlock;
|
|
|
|
|
2009-02-04 22:29:13 +08:00
|
|
|
err = btrfs_init_inode_security(inode, dir);
|
2008-07-25 00:16:36 +08:00
|
|
|
if (err) {
|
|
|
|
drop_inode = 1;
|
|
|
|
goto out_unlock;
|
|
|
|
}
|
|
|
|
|
2007-06-12 18:35:45 +08:00
|
|
|
btrfs_set_trans_block_group(trans, inode);
|
2008-08-05 23:18:09 +08:00
|
|
|
err = btrfs_add_nondir(trans, dentry, inode, 0, index);
|
2007-06-12 18:35:45 +08:00
|
|
|
if (err)
|
|
|
|
drop_inode = 1;
|
|
|
|
else {
|
|
|
|
inode->i_mapping->a_ops = &btrfs_aops;
|
2008-03-26 22:28:07 +08:00
|
|
|
inode->i_mapping->backing_dev_info = &root->fs_info->bdi;
|
2007-06-12 18:35:45 +08:00
|
|
|
inode->i_fop = &btrfs_file_operations;
|
|
|
|
inode->i_op = &btrfs_file_inode_operations;
|
2008-01-25 05:13:08 +08:00
|
|
|
BTRFS_I(inode)->io_tree.ops = &btrfs_extent_io_ops;
|
2007-06-12 18:35:45 +08:00
|
|
|
}
|
|
|
|
btrfs_update_inode_block_group(trans, inode);
|
|
|
|
btrfs_update_inode_block_group(trans, dir);
|
|
|
|
out_unlock:
|
2007-09-17 22:58:06 +08:00
|
|
|
nr = trans->blocks_used;
|
2008-07-30 04:15:18 +08:00
|
|
|
btrfs_end_transaction_throttle(trans, root);
|
2007-12-22 05:27:21 +08:00
|
|
|
fail:
|
2009-09-12 04:12:44 +08:00
|
|
|
btrfs_unreserve_metadata_space(root, 5);
|
2007-06-12 18:35:45 +08:00
|
|
|
if (drop_inode) {
|
|
|
|
inode_dec_link_count(inode);
|
|
|
|
iput(inode);
|
|
|
|
}
|
2007-09-17 22:58:06 +08:00
|
|
|
btrfs_btree_balance_dirty(root, nr);
|
2007-06-12 18:35:45 +08:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int btrfs_link(struct dentry *old_dentry, struct inode *dir,
|
|
|
|
struct dentry *dentry)
|
|
|
|
{
|
|
|
|
struct btrfs_trans_handle *trans;
|
|
|
|
struct btrfs_root *root = BTRFS_I(dir)->root;
|
|
|
|
struct inode *inode = old_dentry->d_inode;
|
2008-08-05 23:18:09 +08:00
|
|
|
u64 index;
|
2007-12-22 05:27:21 +08:00
|
|
|
unsigned long nr = 0;
|
2007-06-12 18:35:45 +08:00
|
|
|
int err;
|
|
|
|
int drop_inode = 0;
|
|
|
|
|
|
|
|
if (inode->i_nlink == 0)
|
|
|
|
return -ENOENT;
|
|
|
|
|
2009-09-12 04:12:44 +08:00
|
|
|
/*
|
|
|
|
* 1 item for inode ref
|
|
|
|
* 2 items for dir items
|
|
|
|
*/
|
|
|
|
err = btrfs_reserve_metadata_space(root, 3);
|
2007-12-22 05:27:21 +08:00
|
|
|
if (err)
|
2009-09-12 04:12:44 +08:00
|
|
|
return err;
|
|
|
|
|
|
|
|
btrfs_inc_nlink(inode);
|
|
|
|
|
2008-11-18 10:02:50 +08:00
|
|
|
err = btrfs_set_inode_index(dir, &index);
|
2008-07-25 00:12:38 +08:00
|
|
|
if (err)
|
|
|
|
goto fail;
|
|
|
|
|
2007-06-12 18:35:45 +08:00
|
|
|
trans = btrfs_start_transaction(root, 1);
|
2007-10-16 04:14:19 +08:00
|
|
|
|
2007-06-12 18:35:45 +08:00
|
|
|
btrfs_set_trans_block_group(trans, dir);
|
|
|
|
atomic_inc(&inode->i_count);
|
2008-07-25 00:12:38 +08:00
|
|
|
|
2008-08-05 23:18:09 +08:00
|
|
|
err = btrfs_add_nondir(trans, dentry, inode, 1, index);
|
2007-10-16 04:14:19 +08:00
|
|
|
|
2009-09-24 21:17:31 +08:00
|
|
|
if (err) {
|
2007-06-23 02:16:25 +08:00
|
|
|
drop_inode = 1;
|
2009-09-24 21:17:31 +08:00
|
|
|
} else {
|
|
|
|
btrfs_update_inode_block_group(trans, dir);
|
|
|
|
err = btrfs_update_inode(trans, root, inode);
|
|
|
|
BUG_ON(err);
|
|
|
|
btrfs_log_new_name(trans, inode, NULL, dentry->d_parent);
|
|
|
|
}
|
2007-06-12 18:35:45 +08:00
|
|
|
|
2007-09-17 22:58:06 +08:00
|
|
|
nr = trans->blocks_used;
|
2008-07-30 04:15:18 +08:00
|
|
|
btrfs_end_transaction_throttle(trans, root);
|
2007-12-22 05:27:21 +08:00
|
|
|
fail:
|
2009-09-12 04:12:44 +08:00
|
|
|
btrfs_unreserve_metadata_space(root, 3);
|
2007-06-12 18:35:45 +08:00
|
|
|
if (drop_inode) {
|
|
|
|
inode_dec_link_count(inode);
|
|
|
|
iput(inode);
|
|
|
|
}
|
2007-09-17 22:58:06 +08:00
|
|
|
btrfs_btree_balance_dirty(root, nr);
|
2007-06-12 18:35:45 +08:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int btrfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
|
|
|
|
{
|
2008-05-03 04:13:49 +08:00
|
|
|
struct inode *inode = NULL;
|
2007-06-12 18:35:45 +08:00
|
|
|
struct btrfs_trans_handle *trans;
|
|
|
|
struct btrfs_root *root = BTRFS_I(dir)->root;
|
|
|
|
int err = 0;
|
|
|
|
int drop_on_err = 0;
|
2008-05-03 04:13:49 +08:00
|
|
|
u64 objectid = 0;
|
2008-08-05 23:18:09 +08:00
|
|
|
u64 index = 0;
|
2007-09-17 22:58:06 +08:00
|
|
|
unsigned long nr = 1;
|
2007-06-12 18:35:45 +08:00
|
|
|
|
2009-09-12 04:12:44 +08:00
|
|
|
/*
|
|
|
|
* 2 items for inode and ref
|
|
|
|
* 2 items for dir items
|
|
|
|
* 1 for xattr if selinux is on
|
|
|
|
*/
|
|
|
|
err = btrfs_reserve_metadata_space(root, 5);
|
2007-12-22 05:27:21 +08:00
|
|
|
if (err)
|
2009-09-12 04:12:44 +08:00
|
|
|
return err;
|
2007-12-22 05:27:21 +08:00
|
|
|
|
2007-06-12 18:35:45 +08:00
|
|
|
trans = btrfs_start_transaction(root, 1);
|
2009-09-12 04:12:44 +08:00
|
|
|
if (!trans) {
|
|
|
|
err = -ENOMEM;
|
2007-06-12 18:35:45 +08:00
|
|
|
goto out_unlock;
|
|
|
|
}
|
2009-09-12 04:12:44 +08:00
|
|
|
btrfs_set_trans_block_group(trans, dir);
|
2007-06-12 18:35:45 +08:00
|
|
|
|
|
|
|
err = btrfs_find_free_objectid(trans, root, dir->i_ino, &objectid);
|
|
|
|
if (err) {
|
|
|
|
err = -ENOSPC;
|
|
|
|
goto out_unlock;
|
|
|
|
}
|
|
|
|
|
2008-07-25 00:12:38 +08:00
|
|
|
inode = btrfs_new_inode(trans, root, dir, dentry->d_name.name,
|
2008-01-30 04:15:18 +08:00
|
|
|
dentry->d_name.len,
|
|
|
|
dentry->d_parent->d_inode->i_ino, objectid,
|
2008-08-05 23:18:09 +08:00
|
|
|
BTRFS_I(dir)->block_group, S_IFDIR | mode,
|
|
|
|
&index);
|
2007-06-12 18:35:45 +08:00
|
|
|
if (IS_ERR(inode)) {
|
|
|
|
err = PTR_ERR(inode);
|
|
|
|
goto out_fail;
|
|
|
|
}
|
2007-10-16 04:14:19 +08:00
|
|
|
|
2007-06-12 18:35:45 +08:00
|
|
|
drop_on_err = 1;
|
2008-07-25 00:16:36 +08:00
|
|
|
|
2009-02-04 22:29:13 +08:00
|
|
|
err = btrfs_init_inode_security(inode, dir);
|
2008-07-25 00:16:36 +08:00
|
|
|
if (err)
|
|
|
|
goto out_fail;
|
|
|
|
|
2007-06-12 18:35:45 +08:00
|
|
|
inode->i_op = &btrfs_dir_inode_operations;
|
|
|
|
inode->i_fop = &btrfs_dir_file_operations;
|
|
|
|
btrfs_set_trans_block_group(trans, inode);
|
|
|
|
|
2008-07-18 00:54:05 +08:00
|
|
|
btrfs_i_size_write(inode, 0);
|
2007-06-12 18:35:45 +08:00
|
|
|
err = btrfs_update_inode(trans, root, inode);
|
|
|
|
if (err)
|
|
|
|
goto out_fail;
|
2007-10-16 04:14:19 +08:00
|
|
|
|
2008-09-06 04:13:11 +08:00
|
|
|
err = btrfs_add_link(trans, dentry->d_parent->d_inode,
|
|
|
|
inode, dentry->d_name.name,
|
|
|
|
dentry->d_name.len, 0, index);
|
2007-06-12 18:35:45 +08:00
|
|
|
if (err)
|
|
|
|
goto out_fail;
|
2007-10-16 04:14:19 +08:00
|
|
|
|
2007-06-12 18:35:45 +08:00
|
|
|
d_instantiate(dentry, inode);
|
|
|
|
drop_on_err = 0;
|
|
|
|
btrfs_update_inode_block_group(trans, inode);
|
|
|
|
btrfs_update_inode_block_group(trans, dir);
|
|
|
|
|
|
|
|
out_fail:
|
2007-09-17 22:58:06 +08:00
|
|
|
nr = trans->blocks_used;
|
2008-07-30 04:15:18 +08:00
|
|
|
btrfs_end_transaction_throttle(trans, root);
|
2007-10-16 04:14:19 +08:00
|
|
|
|
2007-06-12 18:35:45 +08:00
|
|
|
out_unlock:
|
2009-09-12 04:12:44 +08:00
|
|
|
btrfs_unreserve_metadata_space(root, 5);
|
2007-06-12 18:35:45 +08:00
|
|
|
if (drop_on_err)
|
|
|
|
iput(inode);
|
2007-09-17 22:58:06 +08:00
|
|
|
btrfs_btree_balance_dirty(root, nr);
|
2007-06-12 18:35:45 +08:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2008-09-30 03:18:18 +08:00
|
|
|
/* helper for btfs_get_extent. Given an existing extent in the tree,
|
|
|
|
* and an extent that you want to insert, deal with overlap and insert
|
|
|
|
* the new extent into the tree.
|
|
|
|
*/
|
2008-04-17 23:29:12 +08:00
|
|
|
static int merge_extent_mapping(struct extent_map_tree *em_tree,
|
|
|
|
struct extent_map *existing,
|
2008-07-18 00:53:50 +08:00
|
|
|
struct extent_map *em,
|
|
|
|
u64 map_start, u64 map_len)
|
2008-04-17 23:29:12 +08:00
|
|
|
{
|
|
|
|
u64 start_diff;
|
|
|
|
|
2008-07-18 00:53:50 +08:00
|
|
|
BUG_ON(map_start < em->start || map_start >= extent_map_end(em));
|
|
|
|
start_diff = map_start - em->start;
|
|
|
|
em->start = map_start;
|
|
|
|
em->len = map_len;
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
if (em->block_start < EXTENT_MAP_LAST_BYTE &&
|
|
|
|
!test_bit(EXTENT_FLAG_COMPRESSED, &em->flags)) {
|
2008-07-18 00:53:50 +08:00
|
|
|
em->block_start += start_diff;
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
em->block_len -= start_diff;
|
|
|
|
}
|
2008-07-18 00:53:50 +08:00
|
|
|
return add_extent_mapping(em_tree, em);
|
2008-04-17 23:29:12 +08:00
|
|
|
}
|
|
|
|
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
static noinline int uncompress_inline(struct btrfs_path *path,
|
|
|
|
struct inode *inode, struct page *page,
|
|
|
|
size_t pg_offset, u64 extent_offset,
|
|
|
|
struct btrfs_file_extent_item *item)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
struct extent_buffer *leaf = path->nodes[0];
|
|
|
|
char *tmp;
|
|
|
|
size_t max_size;
|
|
|
|
unsigned long inline_size;
|
|
|
|
unsigned long ptr;
|
|
|
|
|
|
|
|
WARN_ON(pg_offset != 0);
|
|
|
|
max_size = btrfs_file_extent_ram_bytes(leaf, item);
|
|
|
|
inline_size = btrfs_file_extent_inline_item_len(leaf,
|
|
|
|
btrfs_item_nr(leaf, path->slots[0]));
|
|
|
|
tmp = kmalloc(inline_size, GFP_NOFS);
|
|
|
|
ptr = btrfs_file_extent_inline_start(item);
|
|
|
|
|
|
|
|
read_extent_buffer(leaf, tmp, ptr, inline_size);
|
|
|
|
|
2008-11-11 22:34:41 +08:00
|
|
|
max_size = min_t(unsigned long, PAGE_CACHE_SIZE, max_size);
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
ret = btrfs_zlib_decompress(tmp, page, extent_offset,
|
|
|
|
inline_size, max_size);
|
|
|
|
if (ret) {
|
|
|
|
char *kaddr = kmap_atomic(page, KM_USER0);
|
|
|
|
unsigned long copy_size = min_t(u64,
|
|
|
|
PAGE_CACHE_SIZE - pg_offset,
|
|
|
|
max_size - extent_offset);
|
|
|
|
memset(kaddr + pg_offset, 0, copy_size);
|
|
|
|
kunmap_atomic(kaddr, KM_USER0);
|
|
|
|
}
|
|
|
|
kfree(tmp);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-09-30 03:18:18 +08:00
|
|
|
/*
|
|
|
|
* a bit scary, this does extent mapping from logical file offset to the disk.
|
2009-01-06 10:25:51 +08:00
|
|
|
* the ugly parts come from merging extents from the disk with the in-ram
|
|
|
|
* representation. This gets more complex because of the data=ordered code,
|
2008-09-30 03:18:18 +08:00
|
|
|
* where the in-ram extents might be locked pending data=ordered completion.
|
|
|
|
*
|
|
|
|
* This also copies inline extents directly into the page.
|
|
|
|
*/
|
2009-01-06 10:25:51 +08:00
|
|
|
|
2007-08-28 04:49:44 +08:00
|
|
|
struct extent_map *btrfs_get_extent(struct inode *inode, struct page *page,
|
2008-01-29 22:59:12 +08:00
|
|
|
size_t pg_offset, u64 start, u64 len,
|
2007-08-28 04:49:44 +08:00
|
|
|
int create)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
int err = 0;
|
2007-10-16 04:15:53 +08:00
|
|
|
u64 bytenr;
|
2007-08-28 04:49:44 +08:00
|
|
|
u64 extent_start = 0;
|
|
|
|
u64 extent_end = 0;
|
|
|
|
u64 objectid = inode->i_ino;
|
|
|
|
u32 found_type;
|
2008-07-22 23:18:09 +08:00
|
|
|
struct btrfs_path *path = NULL;
|
2007-08-28 04:49:44 +08:00
|
|
|
struct btrfs_root *root = BTRFS_I(inode)->root;
|
|
|
|
struct btrfs_file_extent_item *item;
|
2007-10-16 04:14:19 +08:00
|
|
|
struct extent_buffer *leaf;
|
|
|
|
struct btrfs_key found_key;
|
2007-08-28 04:49:44 +08:00
|
|
|
struct extent_map *em = NULL;
|
|
|
|
struct extent_map_tree *em_tree = &BTRFS_I(inode)->extent_tree;
|
2008-01-25 05:13:08 +08:00
|
|
|
struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree;
|
2007-08-28 04:49:44 +08:00
|
|
|
struct btrfs_trans_handle *trans = NULL;
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
int compressed;
|
2007-08-28 04:49:44 +08:00
|
|
|
|
|
|
|
again:
|
2009-09-03 04:24:52 +08:00
|
|
|
read_lock(&em_tree->lock);
|
2008-01-25 05:13:08 +08:00
|
|
|
em = lookup_extent_mapping(em_tree, start, len);
|
2008-05-07 23:43:44 +08:00
|
|
|
if (em)
|
|
|
|
em->bdev = root->fs_info->fs_devices->latest_bdev;
|
2009-09-03 04:24:52 +08:00
|
|
|
read_unlock(&em_tree->lock);
|
2008-01-25 05:13:08 +08:00
|
|
|
|
2007-08-28 04:49:44 +08:00
|
|
|
if (em) {
|
2008-04-23 01:26:46 +08:00
|
|
|
if (em->start > start || em->start + em->len <= start)
|
|
|
|
free_extent_map(em);
|
|
|
|
else if (em->block_start == EXTENT_MAP_INLINE && page)
|
2008-01-29 22:59:12 +08:00
|
|
|
free_extent_map(em);
|
|
|
|
else
|
|
|
|
goto out;
|
2007-08-28 04:49:44 +08:00
|
|
|
}
|
2008-01-25 05:13:08 +08:00
|
|
|
em = alloc_extent_map(GFP_NOFS);
|
2007-08-28 04:49:44 +08:00
|
|
|
if (!em) {
|
2008-01-25 05:13:08 +08:00
|
|
|
err = -ENOMEM;
|
|
|
|
goto out;
|
2007-08-28 04:49:44 +08:00
|
|
|
}
|
2008-07-18 00:53:50 +08:00
|
|
|
em->bdev = root->fs_info->fs_devices->latest_bdev;
|
2008-01-25 05:13:08 +08:00
|
|
|
em->start = EXTENT_MAP_HOLE;
|
2008-11-11 00:53:33 +08:00
|
|
|
em->orig_start = EXTENT_MAP_HOLE;
|
2008-01-25 05:13:08 +08:00
|
|
|
em->len = (u64)-1;
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
em->block_len = (u64)-1;
|
2008-07-22 23:18:09 +08:00
|
|
|
|
|
|
|
if (!path) {
|
|
|
|
path = btrfs_alloc_path();
|
|
|
|
BUG_ON(!path);
|
|
|
|
}
|
|
|
|
|
2007-11-01 23:28:41 +08:00
|
|
|
ret = btrfs_lookup_file_extent(trans, root, path,
|
|
|
|
objectid, start, trans != NULL);
|
2007-08-28 04:49:44 +08:00
|
|
|
if (ret < 0) {
|
|
|
|
err = ret;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret != 0) {
|
|
|
|
if (path->slots[0] == 0)
|
|
|
|
goto not_found;
|
|
|
|
path->slots[0]--;
|
|
|
|
}
|
|
|
|
|
2007-10-16 04:14:19 +08:00
|
|
|
leaf = path->nodes[0];
|
|
|
|
item = btrfs_item_ptr(leaf, path->slots[0],
|
2007-08-28 04:49:44 +08:00
|
|
|
struct btrfs_file_extent_item);
|
|
|
|
/* are we inside the extent that was found? */
|
2007-10-16 04:14:19 +08:00
|
|
|
btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
|
|
|
|
found_type = btrfs_key_type(&found_key);
|
|
|
|
if (found_key.objectid != objectid ||
|
2007-08-28 04:49:44 +08:00
|
|
|
found_type != BTRFS_EXTENT_DATA_KEY) {
|
|
|
|
goto not_found;
|
|
|
|
}
|
|
|
|
|
2007-10-16 04:14:19 +08:00
|
|
|
found_type = btrfs_file_extent_type(leaf, item);
|
|
|
|
extent_start = found_key.offset;
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
compressed = btrfs_file_extent_compression(leaf, item);
|
2008-10-31 02:25:28 +08:00
|
|
|
if (found_type == BTRFS_FILE_EXTENT_REG ||
|
|
|
|
found_type == BTRFS_FILE_EXTENT_PREALLOC) {
|
2007-08-28 04:49:44 +08:00
|
|
|
extent_end = extent_start +
|
2007-10-16 04:15:53 +08:00
|
|
|
btrfs_file_extent_num_bytes(leaf, item);
|
2008-10-31 02:19:41 +08:00
|
|
|
} else if (found_type == BTRFS_FILE_EXTENT_INLINE) {
|
|
|
|
size_t size;
|
|
|
|
size = btrfs_file_extent_inline_len(leaf, item);
|
|
|
|
extent_end = (extent_start + size + root->sectorsize - 1) &
|
|
|
|
~((u64)root->sectorsize - 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (start >= extent_end) {
|
|
|
|
path->slots[0]++;
|
|
|
|
if (path->slots[0] >= btrfs_header_nritems(leaf)) {
|
|
|
|
ret = btrfs_next_leaf(root, path);
|
|
|
|
if (ret < 0) {
|
|
|
|
err = ret;
|
|
|
|
goto out;
|
2007-08-28 04:49:44 +08:00
|
|
|
}
|
2008-10-31 02:19:41 +08:00
|
|
|
if (ret > 0)
|
|
|
|
goto not_found;
|
|
|
|
leaf = path->nodes[0];
|
2007-08-28 04:49:44 +08:00
|
|
|
}
|
2008-10-31 02:19:41 +08:00
|
|
|
btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
|
|
|
|
if (found_key.objectid != objectid ||
|
|
|
|
found_key.type != BTRFS_EXTENT_DATA_KEY)
|
|
|
|
goto not_found;
|
|
|
|
if (start + len <= found_key.offset)
|
|
|
|
goto not_found;
|
|
|
|
em->start = start;
|
|
|
|
em->len = found_key.offset - start;
|
|
|
|
goto not_found_em;
|
|
|
|
}
|
|
|
|
|
2008-10-31 02:25:28 +08:00
|
|
|
if (found_type == BTRFS_FILE_EXTENT_REG ||
|
|
|
|
found_type == BTRFS_FILE_EXTENT_PREALLOC) {
|
2008-10-31 02:19:41 +08:00
|
|
|
em->start = extent_start;
|
|
|
|
em->len = extent_end - extent_start;
|
2008-11-10 20:34:43 +08:00
|
|
|
em->orig_start = extent_start -
|
|
|
|
btrfs_file_extent_offset(leaf, item);
|
2007-10-16 04:15:53 +08:00
|
|
|
bytenr = btrfs_file_extent_disk_bytenr(leaf, item);
|
|
|
|
if (bytenr == 0) {
|
2007-10-16 04:14:19 +08:00
|
|
|
em->block_start = EXTENT_MAP_HOLE;
|
2007-08-28 04:49:44 +08:00
|
|
|
goto insert;
|
|
|
|
}
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
if (compressed) {
|
|
|
|
set_bit(EXTENT_FLAG_COMPRESSED, &em->flags);
|
|
|
|
em->block_start = bytenr;
|
|
|
|
em->block_len = btrfs_file_extent_disk_num_bytes(leaf,
|
|
|
|
item);
|
|
|
|
} else {
|
|
|
|
bytenr += btrfs_file_extent_offset(leaf, item);
|
|
|
|
em->block_start = bytenr;
|
|
|
|
em->block_len = em->len;
|
2008-10-31 02:25:28 +08:00
|
|
|
if (found_type == BTRFS_FILE_EXTENT_PREALLOC)
|
|
|
|
set_bit(EXTENT_FLAG_PREALLOC, &em->flags);
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
}
|
2007-08-28 04:49:44 +08:00
|
|
|
goto insert;
|
|
|
|
} else if (found_type == BTRFS_FILE_EXTENT_INLINE) {
|
2007-10-16 04:14:19 +08:00
|
|
|
unsigned long ptr;
|
2007-08-28 04:49:44 +08:00
|
|
|
char *map;
|
2007-10-16 04:18:25 +08:00
|
|
|
size_t size;
|
|
|
|
size_t extent_offset;
|
|
|
|
size_t copy_size;
|
2007-08-28 04:49:44 +08:00
|
|
|
|
2007-10-29 23:41:07 +08:00
|
|
|
em->block_start = EXTENT_MAP_INLINE;
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
if (!page || create) {
|
2007-10-29 23:41:07 +08:00
|
|
|
em->start = extent_start;
|
2008-10-31 02:19:41 +08:00
|
|
|
em->len = extent_end - extent_start;
|
2007-10-29 23:41:07 +08:00
|
|
|
goto out;
|
|
|
|
}
|
2007-10-16 04:14:19 +08:00
|
|
|
|
2008-10-31 02:19:41 +08:00
|
|
|
size = btrfs_file_extent_inline_len(leaf, item);
|
|
|
|
extent_offset = page_offset(page) + pg_offset - extent_start;
|
2008-01-29 22:59:12 +08:00
|
|
|
copy_size = min_t(u64, PAGE_CACHE_SIZE - pg_offset,
|
2007-10-16 04:18:25 +08:00
|
|
|
size - extent_offset);
|
|
|
|
em->start = extent_start + extent_offset;
|
2008-01-29 22:59:12 +08:00
|
|
|
em->len = (copy_size + root->sectorsize - 1) &
|
|
|
|
~((u64)root->sectorsize - 1);
|
2008-11-10 20:34:43 +08:00
|
|
|
em->orig_start = EXTENT_MAP_INLINE;
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
if (compressed)
|
|
|
|
set_bit(EXTENT_FLAG_COMPRESSED, &em->flags);
|
2007-10-29 23:41:07 +08:00
|
|
|
ptr = btrfs_file_extent_inline_start(item) + extent_offset;
|
2007-11-01 23:28:41 +08:00
|
|
|
if (create == 0 && !PageUptodate(page)) {
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
if (btrfs_file_extent_compression(leaf, item) ==
|
|
|
|
BTRFS_COMPRESS_ZLIB) {
|
|
|
|
ret = uncompress_inline(path, inode, page,
|
|
|
|
pg_offset,
|
|
|
|
extent_offset, item);
|
|
|
|
BUG_ON(ret);
|
|
|
|
} else {
|
|
|
|
map = kmap(page);
|
|
|
|
read_extent_buffer(leaf, map + pg_offset, ptr,
|
|
|
|
copy_size);
|
2009-09-12 00:36:29 +08:00
|
|
|
if (pg_offset + copy_size < PAGE_CACHE_SIZE) {
|
|
|
|
memset(map + pg_offset + copy_size, 0,
|
|
|
|
PAGE_CACHE_SIZE - pg_offset -
|
|
|
|
copy_size);
|
|
|
|
}
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
kunmap(page);
|
|
|
|
}
|
2007-11-01 23:28:41 +08:00
|
|
|
flush_dcache_page(page);
|
|
|
|
} else if (create && PageUptodate(page)) {
|
|
|
|
if (!trans) {
|
|
|
|
kunmap(page);
|
|
|
|
free_extent_map(em);
|
|
|
|
em = NULL;
|
|
|
|
btrfs_release_path(root, path);
|
2008-07-18 00:54:14 +08:00
|
|
|
trans = btrfs_join_transaction(root, 1);
|
2007-11-01 23:28:41 +08:00
|
|
|
goto again;
|
|
|
|
}
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
map = kmap(page);
|
2008-01-29 22:59:12 +08:00
|
|
|
write_extent_buffer(leaf, map + pg_offset, ptr,
|
2007-11-01 23:28:41 +08:00
|
|
|
copy_size);
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
kunmap(page);
|
2007-11-01 23:28:41 +08:00
|
|
|
btrfs_mark_buffer_dirty(leaf);
|
2007-08-28 04:49:44 +08:00
|
|
|
}
|
2008-01-25 05:13:08 +08:00
|
|
|
set_extent_uptodate(io_tree, em->start,
|
|
|
|
extent_map_end(em) - 1, GFP_NOFS);
|
2007-08-28 04:49:44 +08:00
|
|
|
goto insert;
|
|
|
|
} else {
|
2009-01-06 10:25:51 +08:00
|
|
|
printk(KERN_ERR "btrfs unknown found_type %d\n", found_type);
|
2007-08-28 04:49:44 +08:00
|
|
|
WARN_ON(1);
|
|
|
|
}
|
|
|
|
not_found:
|
|
|
|
em->start = start;
|
2008-01-25 05:13:08 +08:00
|
|
|
em->len = len;
|
2007-08-28 04:49:44 +08:00
|
|
|
not_found_em:
|
2007-10-16 04:14:19 +08:00
|
|
|
em->block_start = EXTENT_MAP_HOLE;
|
2008-10-31 02:19:41 +08:00
|
|
|
set_bit(EXTENT_FLAG_VACANCY, &em->flags);
|
2007-08-28 04:49:44 +08:00
|
|
|
insert:
|
|
|
|
btrfs_release_path(root, path);
|
2008-01-25 05:13:08 +08:00
|
|
|
if (em->start > start || extent_map_end(em) <= start) {
|
2009-01-06 10:25:51 +08:00
|
|
|
printk(KERN_ERR "Btrfs: bad extent! em: [%llu %llu] passed "
|
|
|
|
"[%llu %llu]\n", (unsigned long long)em->start,
|
|
|
|
(unsigned long long)em->len,
|
|
|
|
(unsigned long long)start,
|
|
|
|
(unsigned long long)len);
|
2007-08-28 04:49:44 +08:00
|
|
|
err = -EIO;
|
|
|
|
goto out;
|
|
|
|
}
|
2008-01-25 05:13:08 +08:00
|
|
|
|
|
|
|
err = 0;
|
2009-09-03 04:24:52 +08:00
|
|
|
write_lock(&em_tree->lock);
|
2007-08-28 04:49:44 +08:00
|
|
|
ret = add_extent_mapping(em_tree, em);
|
2008-04-17 23:29:12 +08:00
|
|
|
/* it is possible that someone inserted the extent into the tree
|
|
|
|
* while we had the lock dropped. It is also possible that
|
|
|
|
* an overlapping map exists in the tree
|
|
|
|
*/
|
2007-08-28 04:49:44 +08:00
|
|
|
if (ret == -EEXIST) {
|
2008-04-17 23:29:12 +08:00
|
|
|
struct extent_map *existing;
|
2008-07-18 00:53:50 +08:00
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
2008-04-17 23:29:12 +08:00
|
|
|
existing = lookup_extent_mapping(em_tree, start, len);
|
2008-04-23 01:26:46 +08:00
|
|
|
if (existing && (existing->start > start ||
|
|
|
|
existing->start + existing->len <= start)) {
|
|
|
|
free_extent_map(existing);
|
|
|
|
existing = NULL;
|
|
|
|
}
|
2008-04-17 23:29:12 +08:00
|
|
|
if (!existing) {
|
|
|
|
existing = lookup_extent_mapping(em_tree, em->start,
|
|
|
|
em->len);
|
|
|
|
if (existing) {
|
|
|
|
err = merge_extent_mapping(em_tree, existing,
|
2008-07-18 00:53:50 +08:00
|
|
|
em, start,
|
|
|
|
root->sectorsize);
|
2008-04-17 23:29:12 +08:00
|
|
|
free_extent_map(existing);
|
|
|
|
if (err) {
|
|
|
|
free_extent_map(em);
|
|
|
|
em = NULL;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
err = -EIO;
|
|
|
|
free_extent_map(em);
|
|
|
|
em = NULL;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
free_extent_map(em);
|
|
|
|
em = existing;
|
2008-07-18 00:53:50 +08:00
|
|
|
err = 0;
|
2007-08-28 04:49:44 +08:00
|
|
|
}
|
|
|
|
}
|
2009-09-03 04:24:52 +08:00
|
|
|
write_unlock(&em_tree->lock);
|
2007-08-28 04:49:44 +08:00
|
|
|
out:
|
2008-07-22 23:18:09 +08:00
|
|
|
if (path)
|
|
|
|
btrfs_free_path(path);
|
2007-08-28 04:49:44 +08:00
|
|
|
if (trans) {
|
|
|
|
ret = btrfs_end_transaction(trans, root);
|
2009-01-06 10:25:51 +08:00
|
|
|
if (!err)
|
2007-08-28 04:49:44 +08:00
|
|
|
err = ret;
|
|
|
|
}
|
|
|
|
if (err) {
|
|
|
|
free_extent_map(em);
|
|
|
|
return ERR_PTR(err);
|
|
|
|
}
|
|
|
|
return em;
|
|
|
|
}
|
|
|
|
|
2008-04-10 22:23:21 +08:00
|
|
|
static ssize_t btrfs_direct_IO(int rw, struct kiocb *iocb,
|
|
|
|
const struct iovec *iov, loff_t offset,
|
|
|
|
unsigned long nr_segs)
|
|
|
|
{
|
2008-04-23 01:26:46 +08:00
|
|
|
return -EINVAL;
|
2008-04-10 22:23:21 +08:00
|
|
|
}
|
|
|
|
|
2009-01-22 03:39:14 +08:00
|
|
|
static int btrfs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
|
|
|
|
__u64 start, __u64 len)
|
|
|
|
{
|
|
|
|
return extent_fiemap(inode, fieinfo, start, len, btrfs_get_extent);
|
|
|
|
}
|
|
|
|
|
2007-08-28 04:49:44 +08:00
|
|
|
int btrfs_readpage(struct file *file, struct page *page)
|
2007-06-16 01:50:00 +08:00
|
|
|
{
|
2008-01-25 05:13:08 +08:00
|
|
|
struct extent_io_tree *tree;
|
|
|
|
tree = &BTRFS_I(page->mapping->host)->io_tree;
|
2007-08-28 04:49:44 +08:00
|
|
|
return extent_read_full_page(tree, page, btrfs_get_extent);
|
2007-06-16 01:50:00 +08:00
|
|
|
}
|
2007-12-22 05:27:21 +08:00
|
|
|
|
2007-08-28 04:49:44 +08:00
|
|
|
static int btrfs_writepage(struct page *page, struct writeback_control *wbc)
|
2007-06-12 18:35:45 +08:00
|
|
|
{
|
2008-01-25 05:13:08 +08:00
|
|
|
struct extent_io_tree *tree;
|
2007-08-28 04:49:44 +08:00
|
|
|
|
|
|
|
|
|
|
|
if (current->flags & PF_MEMALLOC) {
|
|
|
|
redirty_page_for_writepage(wbc, page);
|
|
|
|
unlock_page(page);
|
|
|
|
return 0;
|
|
|
|
}
|
2008-01-25 05:13:08 +08:00
|
|
|
tree = &BTRFS_I(page->mapping->host)->io_tree;
|
2007-08-28 04:49:44 +08:00
|
|
|
return extent_write_full_page(tree, page, btrfs_get_extent, wbc);
|
2007-06-16 01:50:00 +08:00
|
|
|
}
|
|
|
|
|
2008-07-22 23:18:09 +08:00
|
|
|
int btrfs_writepages(struct address_space *mapping,
|
|
|
|
struct writeback_control *wbc)
|
2007-11-02 07:45:34 +08:00
|
|
|
{
|
2008-01-25 05:13:08 +08:00
|
|
|
struct extent_io_tree *tree;
|
2008-11-07 11:02:51 +08:00
|
|
|
|
2008-01-25 05:13:08 +08:00
|
|
|
tree = &BTRFS_I(mapping->host)->io_tree;
|
2007-11-02 07:45:34 +08:00
|
|
|
return extent_writepages(tree, mapping, btrfs_get_extent, wbc);
|
|
|
|
}
|
|
|
|
|
2007-11-08 23:59:22 +08:00
|
|
|
static int
|
|
|
|
btrfs_readpages(struct file *file, struct address_space *mapping,
|
|
|
|
struct list_head *pages, unsigned nr_pages)
|
|
|
|
{
|
2008-01-25 05:13:08 +08:00
|
|
|
struct extent_io_tree *tree;
|
|
|
|
tree = &BTRFS_I(mapping->host)->io_tree;
|
2007-11-08 23:59:22 +08:00
|
|
|
return extent_readpages(tree, mapping, pages, nr_pages,
|
|
|
|
btrfs_get_extent);
|
|
|
|
}
|
2008-07-18 00:53:50 +08:00
|
|
|
static int __btrfs_releasepage(struct page *page, gfp_t gfp_flags)
|
2007-06-16 01:50:00 +08:00
|
|
|
{
|
2008-01-25 05:13:08 +08:00
|
|
|
struct extent_io_tree *tree;
|
|
|
|
struct extent_map_tree *map;
|
2007-08-28 04:49:44 +08:00
|
|
|
int ret;
|
2007-06-18 21:57:58 +08:00
|
|
|
|
2008-01-25 05:13:08 +08:00
|
|
|
tree = &BTRFS_I(page->mapping->host)->io_tree;
|
|
|
|
map = &BTRFS_I(page->mapping->host)->extent_tree;
|
2008-01-29 22:59:12 +08:00
|
|
|
ret = try_release_extent_mapping(map, tree, page, gfp_flags);
|
2007-08-28 04:49:44 +08:00
|
|
|
if (ret == 1) {
|
|
|
|
ClearPagePrivate(page);
|
|
|
|
set_page_private(page, 0);
|
|
|
|
page_cache_release(page);
|
2007-06-12 18:35:45 +08:00
|
|
|
}
|
2007-08-28 04:49:44 +08:00
|
|
|
return ret;
|
2007-06-12 18:35:45 +08:00
|
|
|
}
|
|
|
|
|
2008-07-18 00:53:50 +08:00
|
|
|
static int btrfs_releasepage(struct page *page, gfp_t gfp_flags)
|
|
|
|
{
|
2008-09-12 03:51:43 +08:00
|
|
|
if (PageWriteback(page) || PageDirty(page))
|
|
|
|
return 0;
|
2009-02-12 23:06:04 +08:00
|
|
|
return __btrfs_releasepage(page, gfp_flags & GFP_NOFS);
|
2008-07-18 00:53:50 +08:00
|
|
|
}
|
|
|
|
|
2007-08-28 04:49:44 +08:00
|
|
|
static void btrfs_invalidatepage(struct page *page, unsigned long offset)
|
2007-06-12 18:35:45 +08:00
|
|
|
{
|
2008-01-25 05:13:08 +08:00
|
|
|
struct extent_io_tree *tree;
|
2008-07-18 00:53:50 +08:00
|
|
|
struct btrfs_ordered_extent *ordered;
|
|
|
|
u64 page_start = page_offset(page);
|
|
|
|
u64 page_end = page_start + PAGE_CACHE_SIZE - 1;
|
2007-06-12 18:35:45 +08:00
|
|
|
|
2009-09-03 04:53:46 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* we have the page locked, so new writeback can't start,
|
|
|
|
* and the dirty bit won't be cleared while we are here.
|
|
|
|
*
|
|
|
|
* Wait for IO on this page so that we can safely clear
|
|
|
|
* the PagePrivate2 bit and do ordered accounting
|
|
|
|
*/
|
2008-07-18 00:53:50 +08:00
|
|
|
wait_on_page_writeback(page);
|
2009-09-03 04:53:46 +08:00
|
|
|
|
2008-01-25 05:13:08 +08:00
|
|
|
tree = &BTRFS_I(page->mapping->host)->io_tree;
|
2008-07-18 00:53:50 +08:00
|
|
|
if (offset) {
|
|
|
|
btrfs_releasepage(page, GFP_NOFS);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
lock_extent(tree, page_start, page_end, GFP_NOFS);
|
|
|
|
ordered = btrfs_lookup_ordered_extent(page->mapping->host,
|
|
|
|
page_offset(page));
|
|
|
|
if (ordered) {
|
2008-07-18 01:53:27 +08:00
|
|
|
/*
|
|
|
|
* IO on this page will never be started, so we need
|
|
|
|
* to account for any ordered extents now
|
|
|
|
*/
|
2008-07-18 00:53:50 +08:00
|
|
|
clear_extent_bit(tree, page_start, page_end,
|
|
|
|
EXTENT_DIRTY | EXTENT_DELALLOC |
|
2009-09-03 03:04:12 +08:00
|
|
|
EXTENT_LOCKED, 1, 0, NULL, GFP_NOFS);
|
2009-09-03 04:53:46 +08:00
|
|
|
/*
|
|
|
|
* whoever cleared the private bit is responsible
|
|
|
|
* for the finish_ordered_io
|
|
|
|
*/
|
|
|
|
if (TestClearPagePrivate2(page)) {
|
|
|
|
btrfs_finish_ordered_io(page->mapping->host,
|
|
|
|
page_start, page_end);
|
|
|
|
}
|
2008-07-18 00:53:50 +08:00
|
|
|
btrfs_put_ordered_extent(ordered);
|
|
|
|
lock_extent(tree, page_start, page_end, GFP_NOFS);
|
|
|
|
}
|
|
|
|
clear_extent_bit(tree, page_start, page_end,
|
2009-09-03 04:53:46 +08:00
|
|
|
EXTENT_LOCKED | EXTENT_DIRTY | EXTENT_DELALLOC,
|
2009-09-03 03:04:12 +08:00
|
|
|
1, 1, NULL, GFP_NOFS);
|
2008-07-18 00:53:50 +08:00
|
|
|
__btrfs_releasepage(page, GFP_NOFS);
|
|
|
|
|
2008-07-21 22:29:44 +08:00
|
|
|
ClearPageChecked(page);
|
2008-04-19 04:11:30 +08:00
|
|
|
if (PagePrivate(page)) {
|
|
|
|
ClearPagePrivate(page);
|
|
|
|
set_page_private(page, 0);
|
|
|
|
page_cache_release(page);
|
|
|
|
}
|
2007-06-12 18:35:45 +08:00
|
|
|
}
|
|
|
|
|
2007-06-16 01:50:00 +08:00
|
|
|
/*
|
|
|
|
* btrfs_page_mkwrite() is not allowed to change the file size as it gets
|
|
|
|
* called from a page fault handler when a page is first dirtied. Hence we must
|
|
|
|
* be careful to check for EOF conditions here. We set the page up correctly
|
|
|
|
* for a written page which means we get ENOSPC checking when writing into
|
|
|
|
* holes and correct delalloc and unwritten extent mapping on filesystems that
|
|
|
|
* support these features.
|
|
|
|
*
|
|
|
|
* We are not allowed to take the i_mutex here so we have to play games to
|
|
|
|
* protect against truncate races as the page could now be beyond EOF. Because
|
|
|
|
* vmtruncate() writes the inode size before removing pages, once we have the
|
|
|
|
* page lock we can determine safely if the page is beyond EOF. If it is not
|
|
|
|
* beyond EOF, then the page is guaranteed safe against truncation until we
|
|
|
|
* unlock the page.
|
|
|
|
*/
|
2009-04-01 06:23:21 +08:00
|
|
|
int btrfs_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)
|
2007-06-16 01:50:00 +08:00
|
|
|
{
|
2009-04-01 06:23:21 +08:00
|
|
|
struct page *page = vmf->page;
|
2007-12-19 05:15:09 +08:00
|
|
|
struct inode *inode = fdentry(vma->vm_file)->d_inode;
|
2007-12-22 05:27:21 +08:00
|
|
|
struct btrfs_root *root = BTRFS_I(inode)->root;
|
2008-07-18 00:53:50 +08:00
|
|
|
struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree;
|
|
|
|
struct btrfs_ordered_extent *ordered;
|
|
|
|
char *kaddr;
|
|
|
|
unsigned long zero_start;
|
2007-06-16 01:50:00 +08:00
|
|
|
loff_t size;
|
2007-12-22 05:27:21 +08:00
|
|
|
int ret;
|
2007-08-28 04:49:44 +08:00
|
|
|
u64 page_start;
|
2008-07-18 00:53:50 +08:00
|
|
|
u64 page_end;
|
2007-06-16 01:50:00 +08:00
|
|
|
|
2009-02-21 00:00:09 +08:00
|
|
|
ret = btrfs_check_data_free_space(root, inode, PAGE_CACHE_SIZE);
|
2009-04-01 06:23:23 +08:00
|
|
|
if (ret) {
|
|
|
|
if (ret == -ENOMEM)
|
|
|
|
ret = VM_FAULT_OOM;
|
|
|
|
else /* -ENOSPC, -EIO, etc */
|
|
|
|
ret = VM_FAULT_SIGBUS;
|
2007-12-22 05:27:21 +08:00
|
|
|
goto out;
|
2009-04-01 06:23:23 +08:00
|
|
|
}
|
2007-12-22 05:27:21 +08:00
|
|
|
|
2009-09-12 04:12:44 +08:00
|
|
|
ret = btrfs_reserve_metadata_for_delalloc(root, inode, 1);
|
|
|
|
if (ret) {
|
|
|
|
btrfs_free_reserved_data_space(root, inode, PAGE_CACHE_SIZE);
|
|
|
|
ret = VM_FAULT_SIGBUS;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2009-04-01 06:23:23 +08:00
|
|
|
ret = VM_FAULT_NOPAGE; /* make the VM retry the fault */
|
2008-07-18 00:53:50 +08:00
|
|
|
again:
|
2007-06-16 01:50:00 +08:00
|
|
|
lock_page(page);
|
|
|
|
size = i_size_read(inode);
|
2008-07-18 00:53:50 +08:00
|
|
|
page_start = page_offset(page);
|
|
|
|
page_end = page_start + PAGE_CACHE_SIZE - 1;
|
2007-08-28 04:49:44 +08:00
|
|
|
|
2007-06-16 01:50:00 +08:00
|
|
|
if ((page->mapping != inode->i_mapping) ||
|
2008-07-18 00:53:50 +08:00
|
|
|
(page_start >= size)) {
|
2009-02-21 00:00:09 +08:00
|
|
|
btrfs_free_reserved_data_space(root, inode, PAGE_CACHE_SIZE);
|
2007-06-16 01:50:00 +08:00
|
|
|
/* page got truncated out from underneath us */
|
|
|
|
goto out_unlock;
|
|
|
|
}
|
2008-07-18 00:53:50 +08:00
|
|
|
wait_on_page_writeback(page);
|
|
|
|
|
|
|
|
lock_extent(io_tree, page_start, page_end, GFP_NOFS);
|
|
|
|
set_page_extent_mapped(page);
|
|
|
|
|
2008-07-18 01:53:27 +08:00
|
|
|
/*
|
|
|
|
* we can't set the delalloc bits if there are pending ordered
|
|
|
|
* extents. Drop our locks and wait for them to finish
|
|
|
|
*/
|
2008-07-18 00:53:50 +08:00
|
|
|
ordered = btrfs_lookup_ordered_extent(inode, page_start);
|
|
|
|
if (ordered) {
|
|
|
|
unlock_extent(io_tree, page_start, page_end, GFP_NOFS);
|
|
|
|
unlock_page(page);
|
2008-07-18 01:53:27 +08:00
|
|
|
btrfs_start_ordered_extent(inode, ordered, 1);
|
2008-07-18 00:53:50 +08:00
|
|
|
btrfs_put_ordered_extent(ordered);
|
|
|
|
goto again;
|
|
|
|
}
|
|
|
|
|
2009-09-12 04:12:44 +08:00
|
|
|
ret = btrfs_set_extent_delalloc(inode, page_start, page_end);
|
|
|
|
if (ret) {
|
|
|
|
unlock_extent(io_tree, page_start, page_end, GFP_NOFS);
|
|
|
|
ret = VM_FAULT_SIGBUS;
|
|
|
|
goto out_unlock;
|
|
|
|
}
|
2008-07-18 00:53:50 +08:00
|
|
|
ret = 0;
|
2007-06-16 01:50:00 +08:00
|
|
|
|
|
|
|
/* page is wholly or partially inside EOF */
|
2007-08-28 04:49:44 +08:00
|
|
|
if (page_start + PAGE_CACHE_SIZE > size)
|
2008-07-18 00:53:50 +08:00
|
|
|
zero_start = size & ~PAGE_CACHE_MASK;
|
2007-06-16 01:50:00 +08:00
|
|
|
else
|
2008-07-18 00:53:50 +08:00
|
|
|
zero_start = PAGE_CACHE_SIZE;
|
2007-06-16 01:50:00 +08:00
|
|
|
|
2008-07-18 00:53:50 +08:00
|
|
|
if (zero_start != PAGE_CACHE_SIZE) {
|
|
|
|
kaddr = kmap(page);
|
|
|
|
memset(kaddr + zero_start, 0, PAGE_CACHE_SIZE - zero_start);
|
|
|
|
flush_dcache_page(page);
|
|
|
|
kunmap(page);
|
|
|
|
}
|
2008-07-18 00:53:51 +08:00
|
|
|
ClearPageChecked(page);
|
2008-07-18 00:53:50 +08:00
|
|
|
set_page_dirty(page);
|
2009-09-12 00:33:12 +08:00
|
|
|
SetPageUptodate(page);
|
2009-04-01 01:27:11 +08:00
|
|
|
|
|
|
|
BTRFS_I(inode)->last_trans = root->fs_info->generation + 1;
|
2008-07-18 00:53:50 +08:00
|
|
|
unlock_extent(io_tree, page_start, page_end, GFP_NOFS);
|
2007-06-16 01:50:00 +08:00
|
|
|
|
|
|
|
out_unlock:
|
2009-09-12 04:12:44 +08:00
|
|
|
btrfs_unreserve_metadata_for_delalloc(root, inode, 1);
|
2009-09-12 00:33:12 +08:00
|
|
|
if (!ret)
|
|
|
|
return VM_FAULT_LOCKED;
|
2007-06-16 01:50:00 +08:00
|
|
|
unlock_page(page);
|
2007-12-22 05:27:21 +08:00
|
|
|
out:
|
2007-06-16 01:50:00 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2007-06-12 18:35:45 +08:00
|
|
|
static void btrfs_truncate(struct inode *inode)
|
|
|
|
{
|
|
|
|
struct btrfs_root *root = BTRFS_I(inode)->root;
|
|
|
|
int ret;
|
|
|
|
struct btrfs_trans_handle *trans;
|
2007-09-17 22:58:06 +08:00
|
|
|
unsigned long nr;
|
2008-07-18 00:54:05 +08:00
|
|
|
u64 mask = root->sectorsize - 1;
|
2007-06-12 18:35:45 +08:00
|
|
|
|
|
|
|
if (!S_ISREG(inode->i_mode))
|
|
|
|
return;
|
|
|
|
if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
|
|
|
|
return;
|
|
|
|
|
|
|
|
btrfs_truncate_page(inode->i_mapping, inode->i_size);
|
2008-07-21 22:29:44 +08:00
|
|
|
btrfs_wait_ordered_range(inode, inode->i_size & (~mask), (u64)-1);
|
2007-06-12 18:35:45 +08:00
|
|
|
|
|
|
|
trans = btrfs_start_transaction(root, 1);
|
2009-04-01 01:27:11 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* setattr is responsible for setting the ordered_data_close flag,
|
|
|
|
* but that is only tested during the last file release. That
|
|
|
|
* could happen well after the next commit, leaving a great big
|
|
|
|
* window where new writes may get lost if someone chooses to write
|
|
|
|
* to this file after truncating to zero
|
|
|
|
*
|
|
|
|
* The inode doesn't have any dirty data here, and so if we commit
|
|
|
|
* this is a noop. If someone immediately starts writing to the inode
|
|
|
|
* it is very likely we'll catch some of their writes in this
|
|
|
|
* transaction, and the commit will find this file on the ordered
|
|
|
|
* data list with good things to send down.
|
|
|
|
*
|
|
|
|
* This is a best effort solution, there is still a window where
|
|
|
|
* using truncate to replace the contents of the file will
|
|
|
|
* end up with a zero length file after a crash.
|
|
|
|
*/
|
|
|
|
if (inode->i_size == 0 && BTRFS_I(inode)->ordered_data_close)
|
|
|
|
btrfs_add_ordered_operation(trans, root, inode);
|
|
|
|
|
2007-06-12 18:35:45 +08:00
|
|
|
btrfs_set_trans_block_group(trans, inode);
|
2008-07-18 00:54:05 +08:00
|
|
|
btrfs_i_size_write(inode, inode->i_size);
|
2007-06-12 18:35:45 +08:00
|
|
|
|
2008-07-25 00:17:14 +08:00
|
|
|
ret = btrfs_orphan_add(trans, inode);
|
|
|
|
if (ret)
|
|
|
|
goto out;
|
2007-06-12 18:35:45 +08:00
|
|
|
/* FIXME, add redo link to tree so we don't leak on crash */
|
2008-09-06 04:13:11 +08:00
|
|
|
ret = btrfs_truncate_inode_items(trans, root, inode, inode->i_size,
|
2008-01-30 04:11:36 +08:00
|
|
|
BTRFS_EXTENT_DATA_KEY);
|
2007-06-12 18:35:45 +08:00
|
|
|
btrfs_update_inode(trans, root, inode);
|
2007-10-16 04:14:19 +08:00
|
|
|
|
2008-07-25 00:17:14 +08:00
|
|
|
ret = btrfs_orphan_del(trans, inode);
|
|
|
|
BUG_ON(ret);
|
|
|
|
|
|
|
|
out:
|
|
|
|
nr = trans->blocks_used;
|
2008-06-26 04:01:31 +08:00
|
|
|
ret = btrfs_end_transaction_throttle(trans, root);
|
2007-06-12 18:35:45 +08:00
|
|
|
BUG_ON(ret);
|
2007-09-17 22:58:06 +08:00
|
|
|
btrfs_btree_balance_dirty(root, nr);
|
2007-06-12 18:35:45 +08:00
|
|
|
}
|
|
|
|
|
2008-09-30 03:18:18 +08:00
|
|
|
/*
|
|
|
|
* create a new subvolume directory/inode (helper for the ioctl).
|
|
|
|
*/
|
2008-12-12 05:30:39 +08:00
|
|
|
int btrfs_create_subvol_root(struct btrfs_trans_handle *trans,
|
2009-09-22 04:00:26 +08:00
|
|
|
struct btrfs_root *new_root,
|
2008-12-12 05:30:39 +08:00
|
|
|
u64 new_dirid, u64 alloc_hint)
|
2007-06-12 18:35:45 +08:00
|
|
|
{
|
|
|
|
struct inode *inode;
|
2009-09-22 04:00:26 +08:00
|
|
|
int err;
|
2008-08-05 23:18:09 +08:00
|
|
|
u64 index = 0;
|
2007-06-12 18:35:45 +08:00
|
|
|
|
2008-07-25 00:12:38 +08:00
|
|
|
inode = btrfs_new_inode(trans, new_root, NULL, "..", 2, new_dirid,
|
2008-12-12 05:30:39 +08:00
|
|
|
new_dirid, alloc_hint, S_IFDIR | 0700, &index);
|
2007-06-23 02:16:25 +08:00
|
|
|
if (IS_ERR(inode))
|
2008-06-12 09:53:53 +08:00
|
|
|
return PTR_ERR(inode);
|
2007-06-12 18:35:45 +08:00
|
|
|
inode->i_op = &btrfs_dir_inode_operations;
|
|
|
|
inode->i_fop = &btrfs_dir_file_operations;
|
|
|
|
|
|
|
|
inode->i_nlink = 1;
|
2008-07-18 00:54:05 +08:00
|
|
|
btrfs_i_size_write(inode, 0);
|
2008-06-10 09:57:42 +08:00
|
|
|
|
2009-09-22 04:00:26 +08:00
|
|
|
err = btrfs_update_inode(trans, new_root, inode);
|
|
|
|
BUG_ON(err);
|
2008-10-10 01:39:39 +08:00
|
|
|
|
2009-09-22 04:00:26 +08:00
|
|
|
iput(inode);
|
2008-10-10 01:39:39 +08:00
|
|
|
return 0;
|
2007-06-12 18:35:45 +08:00
|
|
|
}
|
|
|
|
|
2008-09-30 03:18:18 +08:00
|
|
|
/* helper function for file defrag and space balancing. This
|
|
|
|
* forces readahead on a given range of bytes in an inode
|
|
|
|
*/
|
2007-12-22 05:27:24 +08:00
|
|
|
unsigned long btrfs_force_ra(struct address_space *mapping,
|
2007-09-11 07:58:16 +08:00
|
|
|
struct file_ra_state *ra, struct file *file,
|
|
|
|
pgoff_t offset, pgoff_t last_index)
|
|
|
|
{
|
2008-04-28 21:02:36 +08:00
|
|
|
pgoff_t req_size = last_index - offset + 1;
|
2007-09-11 07:58:16 +08:00
|
|
|
|
|
|
|
page_cache_sync_readahead(mapping, ra, file, offset, req_size);
|
|
|
|
return offset + req_size;
|
|
|
|
}
|
|
|
|
|
2007-06-12 18:35:45 +08:00
|
|
|
struct inode *btrfs_alloc_inode(struct super_block *sb)
|
|
|
|
{
|
|
|
|
struct btrfs_inode *ei;
|
|
|
|
|
|
|
|
ei = kmem_cache_alloc(btrfs_inode_cachep, GFP_NOFS);
|
|
|
|
if (!ei)
|
|
|
|
return NULL;
|
2007-08-11 04:22:09 +08:00
|
|
|
ei->last_trans = 0;
|
2008-09-06 04:13:11 +08:00
|
|
|
ei->logged_trans = 0;
|
2009-09-12 04:12:44 +08:00
|
|
|
ei->delalloc_extents = 0;
|
|
|
|
ei->delalloc_reserved_extents = 0;
|
2008-07-18 00:53:50 +08:00
|
|
|
btrfs_ordered_inode_tree_init(&ei->ordered_tree);
|
2008-07-25 00:17:14 +08:00
|
|
|
INIT_LIST_HEAD(&ei->i_orphan);
|
2009-04-01 01:27:11 +08:00
|
|
|
INIT_LIST_HEAD(&ei->ordered_operations);
|
2007-06-12 18:35:45 +08:00
|
|
|
return &ei->vfs_inode;
|
|
|
|
}
|
|
|
|
|
|
|
|
void btrfs_destroy_inode(struct inode *inode)
|
|
|
|
{
|
2008-07-18 00:53:50 +08:00
|
|
|
struct btrfs_ordered_extent *ordered;
|
2009-04-01 01:27:11 +08:00
|
|
|
struct btrfs_root *root = BTRFS_I(inode)->root;
|
|
|
|
|
2007-06-12 18:35:45 +08:00
|
|
|
WARN_ON(!list_empty(&inode->i_dentry));
|
|
|
|
WARN_ON(inode->i_data.nrpages);
|
|
|
|
|
2009-04-01 01:27:11 +08:00
|
|
|
/*
|
|
|
|
* Make sure we're properly removed from the ordered operation
|
|
|
|
* lists.
|
|
|
|
*/
|
|
|
|
smp_mb();
|
|
|
|
if (!list_empty(&BTRFS_I(inode)->ordered_operations)) {
|
|
|
|
spin_lock(&root->fs_info->ordered_extent_lock);
|
|
|
|
list_del_init(&BTRFS_I(inode)->ordered_operations);
|
|
|
|
spin_unlock(&root->fs_info->ordered_extent_lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
spin_lock(&root->list_lock);
|
2008-07-25 00:17:14 +08:00
|
|
|
if (!list_empty(&BTRFS_I(inode)->i_orphan)) {
|
|
|
|
printk(KERN_ERR "BTRFS: inode %lu: inode still on the orphan"
|
|
|
|
" list\n", inode->i_ino);
|
|
|
|
dump_stack();
|
|
|
|
}
|
2009-04-01 01:27:11 +08:00
|
|
|
spin_unlock(&root->list_lock);
|
2008-07-25 00:17:14 +08:00
|
|
|
|
2009-01-06 10:25:51 +08:00
|
|
|
while (1) {
|
2008-07-18 00:53:50 +08:00
|
|
|
ordered = btrfs_lookup_first_ordered_extent(inode, (u64)-1);
|
|
|
|
if (!ordered)
|
|
|
|
break;
|
|
|
|
else {
|
2009-01-06 10:25:51 +08:00
|
|
|
printk(KERN_ERR "btrfs found ordered "
|
|
|
|
"extent %llu %llu on inode cleanup\n",
|
|
|
|
(unsigned long long)ordered->file_offset,
|
|
|
|
(unsigned long long)ordered->len);
|
2008-07-18 00:53:50 +08:00
|
|
|
btrfs_remove_ordered_extent(inode, ordered);
|
|
|
|
btrfs_put_ordered_extent(ordered);
|
|
|
|
btrfs_put_ordered_extent(ordered);
|
|
|
|
}
|
|
|
|
}
|
Btrfs: Mixed back reference (FORWARD ROLLING FORMAT CHANGE)
This commit introduces a new kind of back reference for btrfs metadata.
Once a filesystem has been mounted with this commit, IT WILL NO LONGER
BE MOUNTABLE BY OLDER KERNELS.
When a tree block in subvolume tree is cow'd, the reference counts of all
extents it points to are increased by one. At transaction commit time,
the old root of the subvolume is recorded in a "dead root" data structure,
and the btree it points to is later walked, dropping reference counts
and freeing any blocks where the reference count goes to 0.
The increments done during cow and decrements done after commit cancel out,
and the walk is a very expensive way to go about freeing the blocks that
are no longer referenced by the new btree root. This commit reduces the
transaction overhead by avoiding the need for dead root records.
When a non-shared tree block is cow'd, we free the old block at once, and the
new block inherits old block's references. When a tree block with reference
count > 1 is cow'd, we increase the reference counts of all extents
the new block points to by one, and decrease the old block's reference count by
one.
This dead tree avoidance code removes the need to modify the reference
counts of lower level extents when a non-shared tree block is cow'd.
But we still need to update back ref for all pointers in the block.
This is because the location of the block is recorded in the back ref
item.
We can solve this by introducing a new type of back ref. The new
back ref provides information about pointer's key, level and in which
tree the pointer lives. This information allow us to find the pointer
by searching the tree. The shortcoming of the new back ref is that it
only works for pointers in tree blocks referenced by their owner trees.
This is mostly a problem for snapshots, where resolving one of these
fuzzy back references would be O(number_of_snapshots) and quite slow.
The solution used here is to use the fuzzy back references in the common
case where a given tree block is only referenced by one root,
and use the full back references when multiple roots have a reference
on a given block.
This commit adds per subvolume red-black tree to keep trace of cached
inodes. The red-black tree helps the balancing code to find cached
inodes whose inode numbers within a given range.
This commit improves the balancing code by introducing several data
structures to keep the state of balancing. The most important one
is the back ref cache. It caches how the upper level tree blocks are
referenced. This greatly reduce the overhead of checking back ref.
The improved balancing code scales significantly better with a large
number of snapshots.
This is a very large commit and was written in a number of
pieces. But, they depend heavily on the disk format change and were
squashed together to make sure git bisect didn't end up in a
bad state wrt space balancing or the format change.
Signed-off-by: Yan Zheng <zheng.yan@oracle.com>
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2009-06-10 22:45:14 +08:00
|
|
|
inode_tree_del(inode);
|
2008-09-26 22:05:38 +08:00
|
|
|
btrfs_drop_extent_cache(inode, 0, (u64)-1, 0);
|
2007-06-12 18:35:45 +08:00
|
|
|
kmem_cache_free(btrfs_inode_cachep, BTRFS_I(inode));
|
|
|
|
}
|
|
|
|
|
2009-09-22 04:00:26 +08:00
|
|
|
void btrfs_drop_inode(struct inode *inode)
|
|
|
|
{
|
|
|
|
struct btrfs_root *root = BTRFS_I(inode)->root;
|
|
|
|
|
|
|
|
if (inode->i_nlink > 0 && btrfs_root_refs(&root->root_item) == 0)
|
|
|
|
generic_delete_inode(inode);
|
|
|
|
else
|
|
|
|
generic_drop_inode(inode);
|
|
|
|
}
|
|
|
|
|
2008-07-31 04:54:26 +08:00
|
|
|
static void init_once(void *foo)
|
2007-06-12 18:35:45 +08:00
|
|
|
{
|
|
|
|
struct btrfs_inode *ei = (struct btrfs_inode *) foo;
|
|
|
|
|
|
|
|
inode_init_once(&ei->vfs_inode);
|
|
|
|
}
|
|
|
|
|
|
|
|
void btrfs_destroy_cachep(void)
|
|
|
|
{
|
|
|
|
if (btrfs_inode_cachep)
|
|
|
|
kmem_cache_destroy(btrfs_inode_cachep);
|
|
|
|
if (btrfs_trans_handle_cachep)
|
|
|
|
kmem_cache_destroy(btrfs_trans_handle_cachep);
|
|
|
|
if (btrfs_transaction_cachep)
|
|
|
|
kmem_cache_destroy(btrfs_transaction_cachep);
|
|
|
|
if (btrfs_path_cachep)
|
|
|
|
kmem_cache_destroy(btrfs_path_cachep);
|
|
|
|
}
|
|
|
|
|
|
|
|
int btrfs_init_cachep(void)
|
|
|
|
{
|
2009-04-13 21:33:09 +08:00
|
|
|
btrfs_inode_cachep = kmem_cache_create("btrfs_inode_cache",
|
|
|
|
sizeof(struct btrfs_inode), 0,
|
|
|
|
SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD, init_once);
|
2007-06-12 18:35:45 +08:00
|
|
|
if (!btrfs_inode_cachep)
|
|
|
|
goto fail;
|
2009-04-13 21:33:09 +08:00
|
|
|
|
|
|
|
btrfs_trans_handle_cachep = kmem_cache_create("btrfs_trans_handle_cache",
|
|
|
|
sizeof(struct btrfs_trans_handle), 0,
|
|
|
|
SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD, NULL);
|
2007-06-12 18:35:45 +08:00
|
|
|
if (!btrfs_trans_handle_cachep)
|
|
|
|
goto fail;
|
2009-04-13 21:33:09 +08:00
|
|
|
|
|
|
|
btrfs_transaction_cachep = kmem_cache_create("btrfs_transaction_cache",
|
|
|
|
sizeof(struct btrfs_transaction), 0,
|
|
|
|
SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD, NULL);
|
2007-06-12 18:35:45 +08:00
|
|
|
if (!btrfs_transaction_cachep)
|
|
|
|
goto fail;
|
2009-04-13 21:33:09 +08:00
|
|
|
|
|
|
|
btrfs_path_cachep = kmem_cache_create("btrfs_path_cache",
|
|
|
|
sizeof(struct btrfs_path), 0,
|
|
|
|
SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD, NULL);
|
2007-06-12 18:35:45 +08:00
|
|
|
if (!btrfs_path_cachep)
|
|
|
|
goto fail;
|
2009-04-13 21:33:09 +08:00
|
|
|
|
2007-06-12 18:35:45 +08:00
|
|
|
return 0;
|
|
|
|
fail:
|
|
|
|
btrfs_destroy_cachep();
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int btrfs_getattr(struct vfsmount *mnt,
|
|
|
|
struct dentry *dentry, struct kstat *stat)
|
|
|
|
{
|
|
|
|
struct inode *inode = dentry->d_inode;
|
|
|
|
generic_fillattr(inode, stat);
|
2008-11-18 09:42:26 +08:00
|
|
|
stat->dev = BTRFS_I(inode)->root->anon_super.s_dev;
|
2008-01-04 03:51:00 +08:00
|
|
|
stat->blksize = PAGE_CACHE_SIZE;
|
2008-10-09 23:46:29 +08:00
|
|
|
stat->blocks = (inode_get_bytes(inode) +
|
|
|
|
BTRFS_I(inode)->delalloc_bytes) >> 9;
|
2007-06-12 18:35:45 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-01-06 10:25:51 +08:00
|
|
|
static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
|
|
|
struct inode *new_dir, struct dentry *new_dentry)
|
2007-06-12 18:35:45 +08:00
|
|
|
{
|
|
|
|
struct btrfs_trans_handle *trans;
|
|
|
|
struct btrfs_root *root = BTRFS_I(old_dir)->root;
|
2009-09-22 03:56:00 +08:00
|
|
|
struct btrfs_root *dest = BTRFS_I(new_dir)->root;
|
2007-06-12 18:35:45 +08:00
|
|
|
struct inode *new_inode = new_dentry->d_inode;
|
|
|
|
struct inode *old_inode = old_dentry->d_inode;
|
|
|
|
struct timespec ctime = CURRENT_TIME;
|
2008-08-05 23:18:09 +08:00
|
|
|
u64 index = 0;
|
2009-09-22 03:56:00 +08:00
|
|
|
u64 root_objectid;
|
2007-06-12 18:35:45 +08:00
|
|
|
int ret;
|
|
|
|
|
2009-09-24 21:17:31 +08:00
|
|
|
if (new_dir->i_ino == BTRFS_EMPTY_SUBVOL_DIR_OBJECTID)
|
|
|
|
return -EPERM;
|
|
|
|
|
2009-09-22 03:56:00 +08:00
|
|
|
/* we only allow rename subvolume link between subvolumes */
|
|
|
|
if (old_inode->i_ino != BTRFS_FIRST_FREE_OBJECTID && root != dest)
|
2008-11-18 09:42:26 +08:00
|
|
|
return -EXDEV;
|
|
|
|
|
2009-09-22 03:56:00 +08:00
|
|
|
if (old_inode->i_ino == BTRFS_EMPTY_SUBVOL_DIR_OBJECTID ||
|
|
|
|
(new_inode && new_inode->i_ino == BTRFS_FIRST_FREE_OBJECTID))
|
2007-06-12 18:35:45 +08:00
|
|
|
return -ENOTEMPTY;
|
2007-10-16 04:14:19 +08:00
|
|
|
|
2009-09-22 03:56:00 +08:00
|
|
|
if (S_ISDIR(old_inode->i_mode) && new_inode &&
|
|
|
|
new_inode->i_size > BTRFS_EMPTY_DIR_SIZE)
|
|
|
|
return -ENOTEMPTY;
|
2008-11-18 09:37:39 +08:00
|
|
|
|
2009-09-12 04:12:44 +08:00
|
|
|
/*
|
|
|
|
* 2 items for dir items
|
|
|
|
* 1 item for orphan entry
|
|
|
|
* 1 item for ref
|
|
|
|
*/
|
|
|
|
ret = btrfs_reserve_metadata_space(root, 4);
|
2007-12-22 05:27:21 +08:00
|
|
|
if (ret)
|
2009-09-22 03:56:00 +08:00
|
|
|
return ret;
|
2007-12-22 05:27:21 +08:00
|
|
|
|
2009-04-01 01:27:11 +08:00
|
|
|
/*
|
|
|
|
* we're using rename to replace one file with another.
|
|
|
|
* and the replacement file is large. Start IO on it now so
|
|
|
|
* we don't add too much work to the end of the transaction
|
|
|
|
*/
|
2009-08-08 01:47:08 +08:00
|
|
|
if (new_inode && S_ISREG(old_inode->i_mode) && new_inode->i_size &&
|
2009-04-01 01:27:11 +08:00
|
|
|
old_inode->i_size > BTRFS_ORDERED_OPERATIONS_FLUSH_LIMIT)
|
|
|
|
filemap_flush(old_inode->i_mapping);
|
|
|
|
|
2009-09-22 04:00:26 +08:00
|
|
|
/* close the racy window with snapshot create/destroy ioctl */
|
|
|
|
if (old_inode->i_ino == BTRFS_FIRST_FREE_OBJECTID)
|
|
|
|
down_read(&root->fs_info->subvol_sem);
|
|
|
|
|
2007-06-12 18:35:45 +08:00
|
|
|
trans = btrfs_start_transaction(root, 1);
|
2009-09-24 21:17:31 +08:00
|
|
|
btrfs_set_trans_block_group(trans, new_dir);
|
2007-10-16 04:14:19 +08:00
|
|
|
|
2009-09-22 03:56:00 +08:00
|
|
|
if (dest != root)
|
|
|
|
btrfs_record_root_in_trans(trans, dest);
|
|
|
|
|
2009-09-24 21:17:31 +08:00
|
|
|
ret = btrfs_set_inode_index(new_dir, &index);
|
|
|
|
if (ret)
|
|
|
|
goto out_fail;
|
2009-04-01 01:27:11 +08:00
|
|
|
|
2009-09-24 21:17:31 +08:00
|
|
|
if (unlikely(old_inode->i_ino == BTRFS_FIRST_FREE_OBJECTID)) {
|
2009-09-22 03:56:00 +08:00
|
|
|
/* force full log commit if subvolume involved. */
|
|
|
|
root->fs_info->last_trans_log_full_commit = trans->transid;
|
|
|
|
} else {
|
2009-09-24 21:17:31 +08:00
|
|
|
ret = btrfs_insert_inode_ref(trans, dest,
|
|
|
|
new_dentry->d_name.name,
|
|
|
|
new_dentry->d_name.len,
|
|
|
|
old_inode->i_ino,
|
|
|
|
new_dir->i_ino, index);
|
|
|
|
if (ret)
|
|
|
|
goto out_fail;
|
2009-09-22 03:56:00 +08:00
|
|
|
/*
|
|
|
|
* this is an ugly little race, but the rename is required
|
|
|
|
* to make sure that if we crash, the inode is either at the
|
|
|
|
* old name or the new one. pinning the log transaction lets
|
|
|
|
* us make sure we don't allow a log commit to come in after
|
|
|
|
* we unlink the name but before we add the new name back in.
|
|
|
|
*/
|
|
|
|
btrfs_pin_log_trans(root);
|
|
|
|
}
|
2009-09-24 21:17:31 +08:00
|
|
|
/*
|
|
|
|
* make sure the inode gets flushed if it is replacing
|
|
|
|
* something.
|
|
|
|
*/
|
|
|
|
if (new_inode && new_inode->i_size &&
|
|
|
|
old_inode && S_ISREG(old_inode->i_mode)) {
|
|
|
|
btrfs_add_ordered_operation(trans, root, old_inode);
|
|
|
|
}
|
2007-06-12 18:35:45 +08:00
|
|
|
|
|
|
|
old_dir->i_ctime = old_dir->i_mtime = ctime;
|
|
|
|
new_dir->i_ctime = new_dir->i_mtime = ctime;
|
|
|
|
old_inode->i_ctime = ctime;
|
2007-10-16 04:14:19 +08:00
|
|
|
|
2009-03-24 22:24:20 +08:00
|
|
|
if (old_dentry->d_parent != new_dentry->d_parent)
|
|
|
|
btrfs_record_unlink_dir(trans, old_dir, old_inode, 1);
|
|
|
|
|
2009-09-22 03:56:00 +08:00
|
|
|
if (unlikely(old_inode->i_ino == BTRFS_FIRST_FREE_OBJECTID)) {
|
|
|
|
root_objectid = BTRFS_I(old_inode)->root->root_key.objectid;
|
|
|
|
ret = btrfs_unlink_subvol(trans, root, old_dir, root_objectid,
|
|
|
|
old_dentry->d_name.name,
|
|
|
|
old_dentry->d_name.len);
|
|
|
|
} else {
|
|
|
|
btrfs_inc_nlink(old_dentry->d_inode);
|
|
|
|
ret = btrfs_unlink_inode(trans, root, old_dir,
|
|
|
|
old_dentry->d_inode,
|
|
|
|
old_dentry->d_name.name,
|
|
|
|
old_dentry->d_name.len);
|
|
|
|
}
|
|
|
|
BUG_ON(ret);
|
2007-06-12 18:35:45 +08:00
|
|
|
|
|
|
|
if (new_inode) {
|
|
|
|
new_inode->i_ctime = CURRENT_TIME;
|
2009-09-22 03:56:00 +08:00
|
|
|
if (unlikely(new_inode->i_ino ==
|
|
|
|
BTRFS_EMPTY_SUBVOL_DIR_OBJECTID)) {
|
|
|
|
root_objectid = BTRFS_I(new_inode)->location.objectid;
|
|
|
|
ret = btrfs_unlink_subvol(trans, dest, new_dir,
|
|
|
|
root_objectid,
|
|
|
|
new_dentry->d_name.name,
|
|
|
|
new_dentry->d_name.len);
|
|
|
|
BUG_ON(new_inode->i_nlink == 0);
|
|
|
|
} else {
|
|
|
|
ret = btrfs_unlink_inode(trans, dest, new_dir,
|
|
|
|
new_dentry->d_inode,
|
|
|
|
new_dentry->d_name.name,
|
|
|
|
new_dentry->d_name.len);
|
|
|
|
}
|
|
|
|
BUG_ON(ret);
|
2008-07-25 00:17:14 +08:00
|
|
|
if (new_inode->i_nlink == 0) {
|
2008-09-06 04:13:11 +08:00
|
|
|
ret = btrfs_orphan_add(trans, new_dentry->d_inode);
|
2009-09-22 03:56:00 +08:00
|
|
|
BUG_ON(ret);
|
2008-07-25 00:17:14 +08:00
|
|
|
}
|
2007-06-12 18:35:45 +08:00
|
|
|
}
|
2008-07-25 00:12:38 +08:00
|
|
|
|
2009-09-22 03:56:00 +08:00
|
|
|
ret = btrfs_add_link(trans, new_dir, old_inode,
|
|
|
|
new_dentry->d_name.name,
|
2009-09-24 21:17:31 +08:00
|
|
|
new_dentry->d_name.len, 0, index);
|
2009-09-22 03:56:00 +08:00
|
|
|
BUG_ON(ret);
|
2007-06-12 18:35:45 +08:00
|
|
|
|
2009-09-22 03:56:00 +08:00
|
|
|
if (old_inode->i_ino != BTRFS_FIRST_FREE_OBJECTID) {
|
|
|
|
btrfs_log_new_name(trans, old_inode, old_dir,
|
|
|
|
new_dentry->d_parent);
|
|
|
|
btrfs_end_log_trans(root);
|
|
|
|
}
|
2009-09-24 21:17:31 +08:00
|
|
|
out_fail:
|
2008-07-30 04:15:18 +08:00
|
|
|
btrfs_end_transaction_throttle(trans, root);
|
2009-09-22 03:56:00 +08:00
|
|
|
|
2009-09-22 04:00:26 +08:00
|
|
|
if (old_inode->i_ino == BTRFS_FIRST_FREE_OBJECTID)
|
|
|
|
up_read(&root->fs_info->subvol_sem);
|
2009-09-12 04:12:44 +08:00
|
|
|
|
|
|
|
btrfs_unreserve_metadata_space(root, 4);
|
2007-06-12 18:35:45 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2008-09-30 03:18:18 +08:00
|
|
|
/*
|
|
|
|
* some fairly slow code that needs optimization. This walks the list
|
|
|
|
* of all the inodes with pending delalloc and forces them to disk.
|
|
|
|
*/
|
2008-08-05 11:17:27 +08:00
|
|
|
int btrfs_start_delalloc_inodes(struct btrfs_root *root)
|
|
|
|
{
|
|
|
|
struct list_head *head = &root->fs_info->delalloc_inodes;
|
|
|
|
struct btrfs_inode *binode;
|
2008-09-26 22:05:38 +08:00
|
|
|
struct inode *inode;
|
2008-08-05 11:17:27 +08:00
|
|
|
|
2008-11-13 03:34:12 +08:00
|
|
|
if (root->fs_info->sb->s_flags & MS_RDONLY)
|
|
|
|
return -EROFS;
|
|
|
|
|
2008-12-16 04:54:40 +08:00
|
|
|
spin_lock(&root->fs_info->delalloc_lock);
|
2009-01-06 10:25:51 +08:00
|
|
|
while (!list_empty(head)) {
|
2008-08-05 11:17:27 +08:00
|
|
|
binode = list_entry(head->next, struct btrfs_inode,
|
|
|
|
delalloc_inodes);
|
2008-09-26 22:05:38 +08:00
|
|
|
inode = igrab(&binode->vfs_inode);
|
|
|
|
if (!inode)
|
|
|
|
list_del_init(&binode->delalloc_inodes);
|
2008-12-16 04:54:40 +08:00
|
|
|
spin_unlock(&root->fs_info->delalloc_lock);
|
2008-09-26 22:05:38 +08:00
|
|
|
if (inode) {
|
2008-09-29 23:19:10 +08:00
|
|
|
filemap_flush(inode->i_mapping);
|
2008-09-26 22:05:38 +08:00
|
|
|
iput(inode);
|
|
|
|
}
|
|
|
|
cond_resched();
|
2008-12-16 04:54:40 +08:00
|
|
|
spin_lock(&root->fs_info->delalloc_lock);
|
2008-08-05 11:17:27 +08:00
|
|
|
}
|
2008-12-16 04:54:40 +08:00
|
|
|
spin_unlock(&root->fs_info->delalloc_lock);
|
2008-09-29 23:19:10 +08:00
|
|
|
|
|
|
|
/* the filemap_flush will queue IO into the worker threads, but
|
|
|
|
* we have to make sure the IO is actually started and that
|
|
|
|
* ordered extents get created before we return
|
|
|
|
*/
|
|
|
|
atomic_inc(&root->fs_info->async_submit_draining);
|
2009-01-06 10:25:51 +08:00
|
|
|
while (atomic_read(&root->fs_info->nr_async_submits) ||
|
2008-11-07 11:02:51 +08:00
|
|
|
atomic_read(&root->fs_info->async_delalloc_pages)) {
|
2008-09-29 23:19:10 +08:00
|
|
|
wait_event(root->fs_info->async_submit_wait,
|
2008-11-07 11:02:51 +08:00
|
|
|
(atomic_read(&root->fs_info->nr_async_submits) == 0 &&
|
|
|
|
atomic_read(&root->fs_info->async_delalloc_pages) == 0));
|
2008-09-29 23:19:10 +08:00
|
|
|
}
|
|
|
|
atomic_dec(&root->fs_info->async_submit_draining);
|
2008-08-05 11:17:27 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-06-12 18:35:45 +08:00
|
|
|
static int btrfs_symlink(struct inode *dir, struct dentry *dentry,
|
|
|
|
const char *symname)
|
|
|
|
{
|
|
|
|
struct btrfs_trans_handle *trans;
|
|
|
|
struct btrfs_root *root = BTRFS_I(dir)->root;
|
|
|
|
struct btrfs_path *path;
|
|
|
|
struct btrfs_key key;
|
2007-12-22 05:27:21 +08:00
|
|
|
struct inode *inode = NULL;
|
2007-06-12 18:35:45 +08:00
|
|
|
int err;
|
|
|
|
int drop_inode = 0;
|
|
|
|
u64 objectid;
|
2008-08-05 23:18:09 +08:00
|
|
|
u64 index = 0 ;
|
2007-06-12 18:35:45 +08:00
|
|
|
int name_len;
|
|
|
|
int datasize;
|
2007-10-16 04:14:19 +08:00
|
|
|
unsigned long ptr;
|
2007-06-12 18:35:45 +08:00
|
|
|
struct btrfs_file_extent_item *ei;
|
2007-10-16 04:14:19 +08:00
|
|
|
struct extent_buffer *leaf;
|
2007-12-22 05:27:21 +08:00
|
|
|
unsigned long nr = 0;
|
2007-06-12 18:35:45 +08:00
|
|
|
|
|
|
|
name_len = strlen(symname) + 1;
|
|
|
|
if (name_len > BTRFS_MAX_INLINE_DATA_SIZE(root))
|
|
|
|
return -ENAMETOOLONG;
|
2007-12-22 05:27:21 +08:00
|
|
|
|
2009-09-12 04:12:44 +08:00
|
|
|
/*
|
|
|
|
* 2 items for inode item and ref
|
|
|
|
* 2 items for dir items
|
|
|
|
* 1 item for xattr if selinux is on
|
|
|
|
*/
|
|
|
|
err = btrfs_reserve_metadata_space(root, 5);
|
2007-12-22 05:27:21 +08:00
|
|
|
if (err)
|
2009-09-12 04:12:44 +08:00
|
|
|
return err;
|
2007-12-22 05:27:21 +08:00
|
|
|
|
2007-06-12 18:35:45 +08:00
|
|
|
trans = btrfs_start_transaction(root, 1);
|
2009-09-12 04:12:44 +08:00
|
|
|
if (!trans)
|
|
|
|
goto out_fail;
|
2007-06-12 18:35:45 +08:00
|
|
|
btrfs_set_trans_block_group(trans, dir);
|
|
|
|
|
|
|
|
err = btrfs_find_free_objectid(trans, root, dir->i_ino, &objectid);
|
|
|
|
if (err) {
|
|
|
|
err = -ENOSPC;
|
|
|
|
goto out_unlock;
|
|
|
|
}
|
|
|
|
|
2008-07-25 00:12:38 +08:00
|
|
|
inode = btrfs_new_inode(trans, root, dir, dentry->d_name.name,
|
2008-01-30 04:15:18 +08:00
|
|
|
dentry->d_name.len,
|
|
|
|
dentry->d_parent->d_inode->i_ino, objectid,
|
2008-08-05 23:18:09 +08:00
|
|
|
BTRFS_I(dir)->block_group, S_IFLNK|S_IRWXUGO,
|
|
|
|
&index);
|
2007-06-12 18:35:45 +08:00
|
|
|
err = PTR_ERR(inode);
|
|
|
|
if (IS_ERR(inode))
|
|
|
|
goto out_unlock;
|
|
|
|
|
2009-02-04 22:29:13 +08:00
|
|
|
err = btrfs_init_inode_security(inode, dir);
|
2008-07-25 00:16:36 +08:00
|
|
|
if (err) {
|
|
|
|
drop_inode = 1;
|
|
|
|
goto out_unlock;
|
|
|
|
}
|
|
|
|
|
2007-06-12 18:35:45 +08:00
|
|
|
btrfs_set_trans_block_group(trans, inode);
|
2008-08-05 23:18:09 +08:00
|
|
|
err = btrfs_add_nondir(trans, dentry, inode, 0, index);
|
2007-06-12 18:35:45 +08:00
|
|
|
if (err)
|
|
|
|
drop_inode = 1;
|
|
|
|
else {
|
|
|
|
inode->i_mapping->a_ops = &btrfs_aops;
|
2008-03-26 22:28:07 +08:00
|
|
|
inode->i_mapping->backing_dev_info = &root->fs_info->bdi;
|
2007-06-12 18:35:45 +08:00
|
|
|
inode->i_fop = &btrfs_file_operations;
|
|
|
|
inode->i_op = &btrfs_file_inode_operations;
|
2008-01-25 05:13:08 +08:00
|
|
|
BTRFS_I(inode)->io_tree.ops = &btrfs_extent_io_ops;
|
2007-06-12 18:35:45 +08:00
|
|
|
}
|
|
|
|
btrfs_update_inode_block_group(trans, inode);
|
|
|
|
btrfs_update_inode_block_group(trans, dir);
|
|
|
|
if (drop_inode)
|
|
|
|
goto out_unlock;
|
|
|
|
|
|
|
|
path = btrfs_alloc_path();
|
|
|
|
BUG_ON(!path);
|
|
|
|
key.objectid = inode->i_ino;
|
|
|
|
key.offset = 0;
|
|
|
|
btrfs_set_key_type(&key, BTRFS_EXTENT_DATA_KEY);
|
|
|
|
datasize = btrfs_file_extent_calc_inline_size(name_len);
|
|
|
|
err = btrfs_insert_empty_item(trans, root, path, &key,
|
|
|
|
datasize);
|
2007-06-23 02:16:25 +08:00
|
|
|
if (err) {
|
|
|
|
drop_inode = 1;
|
|
|
|
goto out_unlock;
|
|
|
|
}
|
2007-10-16 04:14:19 +08:00
|
|
|
leaf = path->nodes[0];
|
|
|
|
ei = btrfs_item_ptr(leaf, path->slots[0],
|
|
|
|
struct btrfs_file_extent_item);
|
|
|
|
btrfs_set_file_extent_generation(leaf, ei, trans->transid);
|
|
|
|
btrfs_set_file_extent_type(leaf, ei,
|
2007-06-12 18:35:45 +08:00
|
|
|
BTRFS_FILE_EXTENT_INLINE);
|
Btrfs: Add zlib compression support
This is a large change for adding compression on reading and writing,
both for inline and regular extents. It does some fairly large
surgery to the writeback paths.
Compression is off by default and enabled by mount -o compress. Even
when the -o compress mount option is not used, it is possible to read
compressed extents off the disk.
If compression for a given set of pages fails to make them smaller, the
file is flagged to avoid future compression attempts later.
* While finding delalloc extents, the pages are locked before being sent down
to the delalloc handler. This allows the delalloc handler to do complex things
such as cleaning the pages, marking them writeback and starting IO on their
behalf.
* Inline extents are inserted at delalloc time now. This allows us to compress
the data before inserting the inline extent, and it allows us to insert
an inline extent that spans multiple pages.
* All of the in-memory extent representations (extent_map.c, ordered-data.c etc)
are changed to record both an in-memory size and an on disk size, as well
as a flag for compression.
From a disk format point of view, the extent pointers in the file are changed
to record the on disk size of a given extent and some encoding flags.
Space in the disk format is allocated for compression encoding, as well
as encryption and a generic 'other' field. Neither the encryption or the
'other' field are currently used.
In order to limit the amount of data read for a single random read in the
file, the size of a compressed extent is limited to 128k. This is a
software only limit, the disk format supports u64 sized compressed extents.
In order to limit the ram consumed while processing extents, the uncompressed
size of a compressed extent is limited to 256k. This is a software only limit
and will be subject to tuning later.
Checksumming is still done on compressed extents, and it is done on the
uncompressed version of the data. This way additional encodings can be
layered on without having to figure out which encoding to checksum.
Compression happens at delalloc time, which is basically singled threaded because
it is usually done by a single pdflush thread. This makes it tricky to
spread the compression load across all the cpus on the box. We'll have to
look at parallel pdflush walks of dirty inodes at a later time.
Decompression is hooked into readpages and it does spread across CPUs nicely.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
2008-10-30 02:49:59 +08:00
|
|
|
btrfs_set_file_extent_encryption(leaf, ei, 0);
|
|
|
|
btrfs_set_file_extent_compression(leaf, ei, 0);
|
|
|
|
btrfs_set_file_extent_other_encoding(leaf, ei, 0);
|
|
|
|
btrfs_set_file_extent_ram_bytes(leaf, ei, name_len);
|
|
|
|
|
2007-06-12 18:35:45 +08:00
|
|
|
ptr = btrfs_file_extent_inline_start(ei);
|
2007-10-16 04:14:19 +08:00
|
|
|
write_extent_buffer(leaf, symname, ptr, name_len);
|
|
|
|
btrfs_mark_buffer_dirty(leaf);
|
2007-06-12 18:35:45 +08:00
|
|
|
btrfs_free_path(path);
|
2007-10-16 04:14:19 +08:00
|
|
|
|
2007-06-12 18:35:45 +08:00
|
|
|
inode->i_op = &btrfs_symlink_inode_operations;
|
|
|
|
inode->i_mapping->a_ops = &btrfs_symlink_aops;
|
2008-03-26 22:28:07 +08:00
|
|
|
inode->i_mapping->backing_dev_info = &root->fs_info->bdi;
|
2008-10-31 02:25:28 +08:00
|
|
|
inode_set_bytes(inode, name_len);
|
2008-07-18 00:54:05 +08:00
|
|
|
btrfs_i_size_write(inode, name_len - 1);
|
2007-06-23 02:16:25 +08:00
|
|
|
err = btrfs_update_inode(trans, root, inode);
|
|
|
|
if (err)
|
|
|
|
drop_inode = 1;
|
2007-06-12 18:35:45 +08:00
|
|
|
|
|
|
|
out_unlock:
|
2007-09-17 22:58:06 +08:00
|
|
|
nr = trans->blocks_used;
|
2008-07-30 04:15:18 +08:00
|
|
|
btrfs_end_transaction_throttle(trans, root);
|
2007-12-22 05:27:21 +08:00
|
|
|
out_fail:
|
2009-09-12 04:12:44 +08:00
|
|
|
btrfs_unreserve_metadata_space(root, 5);
|
2007-06-12 18:35:45 +08:00
|
|
|
if (drop_inode) {
|
|
|
|
inode_dec_link_count(inode);
|
|
|
|
iput(inode);
|
|
|
|
}
|
2007-09-17 22:58:06 +08:00
|
|
|
btrfs_btree_balance_dirty(root, nr);
|
2007-06-12 18:35:45 +08:00
|
|
|
return err;
|
|
|
|
}
|
2008-04-10 22:23:21 +08:00
|
|
|
|
2009-04-21 23:53:38 +08:00
|
|
|
static int prealloc_file_range(struct btrfs_trans_handle *trans,
|
|
|
|
struct inode *inode, u64 start, u64 end,
|
2009-04-25 02:39:24 +08:00
|
|
|
u64 locked_end, u64 alloc_hint, int mode)
|
2008-10-31 02:25:28 +08:00
|
|
|
{
|
|
|
|
struct btrfs_root *root = BTRFS_I(inode)->root;
|
|
|
|
struct btrfs_key ins;
|
|
|
|
u64 alloc_size;
|
|
|
|
u64 cur_offset = start;
|
|
|
|
u64 num_bytes = end - start;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
while (num_bytes > 0) {
|
|
|
|
alloc_size = min(num_bytes, root->fs_info->max_extent);
|
2009-09-12 04:12:44 +08:00
|
|
|
|
|
|
|
ret = btrfs_reserve_metadata_space(root, 1);
|
|
|
|
if (ret)
|
|
|
|
goto out;
|
|
|
|
|
2008-10-31 02:25:28 +08:00
|
|
|
ret = btrfs_reserve_extent(trans, root, alloc_size,
|
|
|
|
root->sectorsize, 0, alloc_hint,
|
|
|
|
(u64)-1, &ins, 1);
|
|
|
|
if (ret) {
|
|
|
|
WARN_ON(1);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
ret = insert_reserved_file_extent(trans, inode,
|
|
|
|
cur_offset, ins.objectid,
|
|
|
|
ins.offset, ins.offset,
|
2009-04-25 02:39:24 +08:00
|
|
|
ins.offset, locked_end,
|
|
|
|
0, 0, 0,
|
2008-10-31 02:25:28 +08:00
|
|
|
BTRFS_FILE_EXTENT_PREALLOC);
|
|
|
|
BUG_ON(ret);
|
2009-09-12 00:27:37 +08:00
|
|
|
btrfs_drop_extent_cache(inode, cur_offset,
|
|
|
|
cur_offset + ins.offset -1, 0);
|
2008-10-31 02:25:28 +08:00
|
|
|
num_bytes -= ins.offset;
|
|
|
|
cur_offset += ins.offset;
|
|
|
|
alloc_hint = ins.objectid + ins.offset;
|
2009-09-12 04:12:44 +08:00
|
|
|
btrfs_unreserve_metadata_space(root, 1);
|
2008-10-31 02:25:28 +08:00
|
|
|
}
|
|
|
|
out:
|
|
|
|
if (cur_offset > start) {
|
|
|
|
inode->i_ctime = CURRENT_TIME;
|
2009-04-17 16:37:41 +08:00
|
|
|
BTRFS_I(inode)->flags |= BTRFS_INODE_PREALLOC;
|
2008-10-31 02:25:28 +08:00
|
|
|
if (!(mode & FALLOC_FL_KEEP_SIZE) &&
|
|
|
|
cur_offset > i_size_read(inode))
|
|
|
|
btrfs_i_size_write(inode, cur_offset);
|
|
|
|
ret = btrfs_update_inode(trans, root, inode);
|
|
|
|
BUG_ON(ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static long btrfs_fallocate(struct inode *inode, int mode,
|
|
|
|
loff_t offset, loff_t len)
|
|
|
|
{
|
|
|
|
u64 cur_offset;
|
|
|
|
u64 last_byte;
|
|
|
|
u64 alloc_start;
|
|
|
|
u64 alloc_end;
|
|
|
|
u64 alloc_hint = 0;
|
2009-04-25 02:39:24 +08:00
|
|
|
u64 locked_end;
|
2008-10-31 02:25:28 +08:00
|
|
|
u64 mask = BTRFS_I(inode)->root->sectorsize - 1;
|
|
|
|
struct extent_map *em;
|
2009-04-21 23:53:38 +08:00
|
|
|
struct btrfs_trans_handle *trans;
|
2009-06-28 09:07:34 +08:00
|
|
|
struct btrfs_root *root;
|
2008-10-31 02:25:28 +08:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
alloc_start = offset & ~mask;
|
|
|
|
alloc_end = (offset + len + mask) & ~mask;
|
|
|
|
|
2009-04-21 23:53:38 +08:00
|
|
|
/*
|
|
|
|
* wait for ordered IO before we have any locks. We'll loop again
|
|
|
|
* below with the locks held.
|
|
|
|
*/
|
|
|
|
btrfs_wait_ordered_range(inode, alloc_start, alloc_end - alloc_start);
|
|
|
|
|
2008-10-31 02:25:28 +08:00
|
|
|
mutex_lock(&inode->i_mutex);
|
|
|
|
if (alloc_start > inode->i_size) {
|
|
|
|
ret = btrfs_cont_expand(inode, alloc_start);
|
|
|
|
if (ret)
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2009-06-28 09:07:34 +08:00
|
|
|
root = BTRFS_I(inode)->root;
|
|
|
|
|
|
|
|
ret = btrfs_check_data_free_space(root, inode,
|
|
|
|
alloc_end - alloc_start);
|
|
|
|
if (ret)
|
|
|
|
goto out;
|
|
|
|
|
2009-04-25 02:39:24 +08:00
|
|
|
locked_end = alloc_end - 1;
|
2008-10-31 02:25:28 +08:00
|
|
|
while (1) {
|
|
|
|
struct btrfs_ordered_extent *ordered;
|
2009-04-21 23:53:38 +08:00
|
|
|
|
|
|
|
trans = btrfs_start_transaction(BTRFS_I(inode)->root, 1);
|
|
|
|
if (!trans) {
|
|
|
|
ret = -EIO;
|
2009-06-28 09:07:34 +08:00
|
|
|
goto out_free;
|
2009-04-21 23:53:38 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* the extent lock is ordered inside the running
|
|
|
|
* transaction
|
|
|
|
*/
|
2009-04-25 02:39:24 +08:00
|
|
|
lock_extent(&BTRFS_I(inode)->io_tree, alloc_start, locked_end,
|
|
|
|
GFP_NOFS);
|
2008-10-31 02:25:28 +08:00
|
|
|
ordered = btrfs_lookup_first_ordered_extent(inode,
|
|
|
|
alloc_end - 1);
|
|
|
|
if (ordered &&
|
|
|
|
ordered->file_offset + ordered->len > alloc_start &&
|
|
|
|
ordered->file_offset < alloc_end) {
|
|
|
|
btrfs_put_ordered_extent(ordered);
|
|
|
|
unlock_extent(&BTRFS_I(inode)->io_tree,
|
2009-04-25 02:39:24 +08:00
|
|
|
alloc_start, locked_end, GFP_NOFS);
|
2009-04-21 23:53:38 +08:00
|
|
|
btrfs_end_transaction(trans, BTRFS_I(inode)->root);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* we can't wait on the range with the transaction
|
|
|
|
* running or with the extent lock held
|
|
|
|
*/
|
2008-10-31 02:25:28 +08:00
|
|
|
btrfs_wait_ordered_range(inode, alloc_start,
|
|
|
|
alloc_end - alloc_start);
|
|
|
|
} else {
|
|
|
|
if (ordered)
|
|
|
|
btrfs_put_ordered_extent(ordered);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
cur_offset = alloc_start;
|
|
|
|
while (1) {
|
|
|
|
em = btrfs_get_extent(inode, NULL, 0, cur_offset,
|
|
|
|
alloc_end - cur_offset, 0);
|
|
|
|
BUG_ON(IS_ERR(em) || !em);
|
|
|
|
last_byte = min(extent_map_end(em), alloc_end);
|
|
|
|
last_byte = (last_byte + mask) & ~mask;
|
|
|
|
if (em->block_start == EXTENT_MAP_HOLE) {
|
2009-04-21 23:53:38 +08:00
|
|
|
ret = prealloc_file_range(trans, inode, cur_offset,
|
2009-04-25 02:39:24 +08:00
|
|
|
last_byte, locked_end + 1,
|
|
|
|
alloc_hint, mode);
|
2008-10-31 02:25:28 +08:00
|
|
|
if (ret < 0) {
|
|
|
|
free_extent_map(em);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (em->block_start <= EXTENT_MAP_LAST_BYTE)
|
|
|
|
alloc_hint = em->block_start;
|
|
|
|
free_extent_map(em);
|
|
|
|
|
|
|
|
cur_offset = last_byte;
|
|
|
|
if (cur_offset >= alloc_end) {
|
|
|
|
ret = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2009-04-25 02:39:24 +08:00
|
|
|
unlock_extent(&BTRFS_I(inode)->io_tree, alloc_start, locked_end,
|
2008-10-31 02:25:28 +08:00
|
|
|
GFP_NOFS);
|
2009-04-21 23:53:38 +08:00
|
|
|
|
|
|
|
btrfs_end_transaction(trans, BTRFS_I(inode)->root);
|
2009-06-28 09:07:34 +08:00
|
|
|
out_free:
|
|
|
|
btrfs_free_reserved_data_space(root, inode, alloc_end - alloc_start);
|
2008-10-31 02:25:28 +08:00
|
|
|
out:
|
|
|
|
mutex_unlock(&inode->i_mutex);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2008-07-18 00:53:50 +08:00
|
|
|
static int btrfs_set_page_dirty(struct page *page)
|
|
|
|
{
|
|
|
|
return __set_page_dirty_nobuffers(page);
|
|
|
|
}
|
|
|
|
|
2008-07-31 04:54:26 +08:00
|
|
|
static int btrfs_permission(struct inode *inode, int mask)
|
2008-01-15 02:26:08 +08:00
|
|
|
{
|
2009-04-17 16:37:41 +08:00
|
|
|
if ((BTRFS_I(inode)->flags & BTRFS_INODE_READONLY) && (mask & MAY_WRITE))
|
2008-01-15 02:26:08 +08:00
|
|
|
return -EACCES;
|
2008-07-25 00:16:36 +08:00
|
|
|
return generic_permission(inode, mask, btrfs_check_acl);
|
2008-01-15 02:26:08 +08:00
|
|
|
}
|
2007-06-12 18:35:45 +08:00
|
|
|
|
|
|
|
static struct inode_operations btrfs_dir_inode_operations = {
|
2008-11-18 09:42:26 +08:00
|
|
|
.getattr = btrfs_getattr,
|
2007-06-12 18:35:45 +08:00
|
|
|
.lookup = btrfs_lookup,
|
|
|
|
.create = btrfs_create,
|
|
|
|
.unlink = btrfs_unlink,
|
|
|
|
.link = btrfs_link,
|
|
|
|
.mkdir = btrfs_mkdir,
|
|
|
|
.rmdir = btrfs_rmdir,
|
|
|
|
.rename = btrfs_rename,
|
|
|
|
.symlink = btrfs_symlink,
|
|
|
|
.setattr = btrfs_setattr,
|
2007-07-11 22:18:17 +08:00
|
|
|
.mknod = btrfs_mknod,
|
2008-08-28 18:21:17 +08:00
|
|
|
.setxattr = btrfs_setxattr,
|
|
|
|
.getxattr = btrfs_getxattr,
|
2007-11-17 00:45:54 +08:00
|
|
|
.listxattr = btrfs_listxattr,
|
2008-08-28 18:21:17 +08:00
|
|
|
.removexattr = btrfs_removexattr,
|
2008-01-15 02:26:08 +08:00
|
|
|
.permission = btrfs_permission,
|
2007-06-12 18:35:45 +08:00
|
|
|
};
|
|
|
|
static struct inode_operations btrfs_dir_ro_inode_operations = {
|
|
|
|
.lookup = btrfs_lookup,
|
2008-01-15 02:26:08 +08:00
|
|
|
.permission = btrfs_permission,
|
2007-06-12 18:35:45 +08:00
|
|
|
};
|
2009-09-22 04:00:26 +08:00
|
|
|
|
2007-06-12 18:35:45 +08:00
|
|
|
static struct file_operations btrfs_dir_file_operations = {
|
|
|
|
.llseek = generic_file_llseek,
|
|
|
|
.read = generic_read_dir,
|
2008-08-07 02:42:33 +08:00
|
|
|
.readdir = btrfs_real_readdir,
|
2007-09-14 22:22:47 +08:00
|
|
|
.unlocked_ioctl = btrfs_ioctl,
|
2007-06-12 18:35:45 +08:00
|
|
|
#ifdef CONFIG_COMPAT
|
2007-09-14 22:22:47 +08:00
|
|
|
.compat_ioctl = btrfs_ioctl,
|
2007-06-12 18:35:45 +08:00
|
|
|
#endif
|
2008-06-10 22:07:39 +08:00
|
|
|
.release = btrfs_release_file,
|
2008-09-06 04:13:11 +08:00
|
|
|
.fsync = btrfs_sync_file,
|
2007-06-12 18:35:45 +08:00
|
|
|
};
|
|
|
|
|
2008-01-25 05:13:08 +08:00
|
|
|
static struct extent_io_ops btrfs_extent_io_ops = {
|
2007-08-30 20:50:51 +08:00
|
|
|
.fill_delalloc = run_delalloc_range,
|
2008-02-21 01:07:25 +08:00
|
|
|
.submit_bio_hook = btrfs_submit_bio_hook,
|
2008-03-25 03:02:07 +08:00
|
|
|
.merge_bio_hook = btrfs_merge_bio_hook,
|
2007-08-30 20:50:51 +08:00
|
|
|
.readpage_end_io_hook = btrfs_readpage_end_io_hook,
|
2008-07-18 00:53:50 +08:00
|
|
|
.writepage_end_io_hook = btrfs_writepage_end_io_hook,
|
2008-07-18 00:53:51 +08:00
|
|
|
.writepage_start_hook = btrfs_writepage_start_hook,
|
2008-05-13 01:39:03 +08:00
|
|
|
.readpage_io_failed_hook = btrfs_io_failed_hook,
|
2008-02-01 00:05:37 +08:00
|
|
|
.set_bit_hook = btrfs_set_bit_hook,
|
|
|
|
.clear_bit_hook = btrfs_clear_bit_hook,
|
2009-09-12 04:12:44 +08:00
|
|
|
.merge_extent_hook = btrfs_merge_extent_hook,
|
|
|
|
.split_extent_hook = btrfs_split_extent_hook,
|
2007-08-30 20:50:51 +08:00
|
|
|
};
|
|
|
|
|
2009-01-22 02:11:13 +08:00
|
|
|
/*
|
|
|
|
* btrfs doesn't support the bmap operation because swapfiles
|
|
|
|
* use bmap to make a mapping of extents in the file. They assume
|
|
|
|
* these extents won't change over the life of the file and they
|
|
|
|
* use the bmap result to do IO directly to the drive.
|
|
|
|
*
|
|
|
|
* the btrfs bmap call would return logical addresses that aren't
|
|
|
|
* suitable for IO and they also will change frequently as COW
|
|
|
|
* operations happen. So, swapfile + btrfs == corruption.
|
|
|
|
*
|
|
|
|
* For now we're avoiding this by dropping bmap.
|
|
|
|
*/
|
2007-06-12 18:35:45 +08:00
|
|
|
static struct address_space_operations btrfs_aops = {
|
|
|
|
.readpage = btrfs_readpage,
|
|
|
|
.writepage = btrfs_writepage,
|
2007-11-02 07:45:34 +08:00
|
|
|
.writepages = btrfs_writepages,
|
2007-11-08 23:59:22 +08:00
|
|
|
.readpages = btrfs_readpages,
|
2007-06-12 18:35:45 +08:00
|
|
|
.sync_page = block_sync_page,
|
2008-04-10 22:23:21 +08:00
|
|
|
.direct_IO = btrfs_direct_IO,
|
2007-08-28 04:49:44 +08:00
|
|
|
.invalidatepage = btrfs_invalidatepage,
|
|
|
|
.releasepage = btrfs_releasepage,
|
2008-07-18 00:53:50 +08:00
|
|
|
.set_page_dirty = btrfs_set_page_dirty,
|
2007-06-12 18:35:45 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
static struct address_space_operations btrfs_symlink_aops = {
|
|
|
|
.readpage = btrfs_readpage,
|
|
|
|
.writepage = btrfs_writepage,
|
2007-08-30 23:54:02 +08:00
|
|
|
.invalidatepage = btrfs_invalidatepage,
|
|
|
|
.releasepage = btrfs_releasepage,
|
2007-06-12 18:35:45 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
static struct inode_operations btrfs_file_inode_operations = {
|
|
|
|
.truncate = btrfs_truncate,
|
|
|
|
.getattr = btrfs_getattr,
|
|
|
|
.setattr = btrfs_setattr,
|
2008-08-28 18:21:17 +08:00
|
|
|
.setxattr = btrfs_setxattr,
|
|
|
|
.getxattr = btrfs_getxattr,
|
2007-11-17 00:45:54 +08:00
|
|
|
.listxattr = btrfs_listxattr,
|
2008-08-28 18:21:17 +08:00
|
|
|
.removexattr = btrfs_removexattr,
|
2008-01-15 02:26:08 +08:00
|
|
|
.permission = btrfs_permission,
|
2008-10-31 02:25:28 +08:00
|
|
|
.fallocate = btrfs_fallocate,
|
2009-01-22 03:39:14 +08:00
|
|
|
.fiemap = btrfs_fiemap,
|
2007-06-12 18:35:45 +08:00
|
|
|
};
|
2007-07-11 22:18:17 +08:00
|
|
|
static struct inode_operations btrfs_special_inode_operations = {
|
|
|
|
.getattr = btrfs_getattr,
|
|
|
|
.setattr = btrfs_setattr,
|
2008-01-15 02:26:08 +08:00
|
|
|
.permission = btrfs_permission,
|
2008-08-28 18:21:17 +08:00
|
|
|
.setxattr = btrfs_setxattr,
|
|
|
|
.getxattr = btrfs_getxattr,
|
2008-07-25 00:16:36 +08:00
|
|
|
.listxattr = btrfs_listxattr,
|
2008-08-28 18:21:17 +08:00
|
|
|
.removexattr = btrfs_removexattr,
|
2007-07-11 22:18:17 +08:00
|
|
|
};
|
2007-06-12 18:35:45 +08:00
|
|
|
static struct inode_operations btrfs_symlink_inode_operations = {
|
|
|
|
.readlink = generic_readlink,
|
|
|
|
.follow_link = page_follow_link_light,
|
|
|
|
.put_link = page_put_link,
|
2008-01-15 02:26:08 +08:00
|
|
|
.permission = btrfs_permission,
|
2009-02-04 22:29:13 +08:00
|
|
|
.setxattr = btrfs_setxattr,
|
|
|
|
.getxattr = btrfs_getxattr,
|
|
|
|
.listxattr = btrfs_listxattr,
|
|
|
|
.removexattr = btrfs_removexattr,
|
2007-06-12 18:35:45 +08:00
|
|
|
};
|
2009-09-22 04:00:26 +08:00
|
|
|
|
|
|
|
struct dentry_operations btrfs_dentry_operations = {
|
|
|
|
.d_delete = btrfs_dentry_delete,
|
|
|
|
};
|