Skip to content

Commit

Permalink
Btrfs: Per file/directory controls for COW and compression
Browse files Browse the repository at this point in the history
Data compression and data cow are controlled across the entire FS by mount
options right now.  ioctls are needed to set this on a per file or per
directory basis.  This has been proposed previously, but VFS developers
wanted us to use generic ioctls rather than btrfs-specific ones.

According to Chris's comment, there should be just one true compression
method(probably LZO) stored in the super.  However, before this, we would
wait for that one method is stable enough to be adopted into the super.
So I list it as a long term goal, and just store it in ram today.

After applying this patch, we can use the generic "FS_IOC_SETFLAGS" ioctl to
control file and directory's datacow and compression attribute.

NOTE:
 - The compression type is selected by such rules:
   If we mount btrfs with compress options, ie, zlib/lzo, the type is it.
   Otherwise, we'll use the default compress type (zlib today).

v1->v2:
- rebase to the latest btrfs.
v2->v3:
- fix a problem, i.e. when a file is set NOCOW via mount option, then this NOCOW
  will be screwed by inheritance from parent directory.

Signed-off-by: Liu Bo <liubo2009@cn.fujitsu.com>
Signed-off-by: Chris Mason <chris.mason@oracle.com>
  • Loading branch information
Liu Bo authored and root committed Mar 28, 2011
1 parent 32471f6 commit 75e7cb7
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 7 deletions.
1 change: 1 addition & 0 deletions fs/btrfs/ctree.h
Original file line number Diff line number Diff line change
Expand Up @@ -1282,6 +1282,7 @@ struct btrfs_root {
#define BTRFS_INODE_NODUMP (1 << 8)
#define BTRFS_INODE_NOATIME (1 << 9)
#define BTRFS_INODE_DIRSYNC (1 << 10)
#define BTRFS_INODE_COMPRESS (1 << 11)

/* some macros to generate set/get funcs for the struct fields. This
* assumes there is a lefoo_to_cpu for every type, so lets make a simple
Expand Down
6 changes: 6 additions & 0 deletions fs/btrfs/disk-io.c
Original file line number Diff line number Diff line change
Expand Up @@ -1854,6 +1854,12 @@ struct btrfs_root *open_ctree(struct super_block *sb,

btrfs_check_super_valid(fs_info, sb->s_flags & MS_RDONLY);

/*
* In the long term, we'll store the compression type in the super
* block, and it'll be used for per file compression control.
*/
fs_info->compress_type = BTRFS_COMPRESS_ZLIB;

ret = btrfs_parse_options(tree_root, options);
if (ret) {
err = ret;
Expand Down
31 changes: 28 additions & 3 deletions fs/btrfs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,8 @@ static noinline int compress_file_range(struct inode *inode,
*/
if (!(BTRFS_I(inode)->flags & BTRFS_INODE_NOCOMPRESS) &&
(btrfs_test_opt(root, COMPRESS) ||
(BTRFS_I(inode)->force_compress))) {
(BTRFS_I(inode)->force_compress) ||
(BTRFS_I(inode)->flags & BTRFS_INODE_COMPRESS))) {
WARN_ON(pages);
pages = kzalloc(sizeof(struct page *) * nr_pages, GFP_NOFS);

Expand Down Expand Up @@ -1256,7 +1257,8 @@ static int run_delalloc_range(struct inode *inode, struct page *locked_page,
ret = run_delalloc_nocow(inode, locked_page, start, end,
page_started, 0, nr_written);
else if (!btrfs_test_opt(root, COMPRESS) &&
!(BTRFS_I(inode)->force_compress))
!(BTRFS_I(inode)->force_compress) &&
!(BTRFS_I(inode)->flags & BTRFS_INODE_COMPRESS))
ret = cow_file_range(inode, locked_page, start, end,
page_started, nr_written, 1);
else
Expand Down Expand Up @@ -4584,7 +4586,8 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,
if ((mode & S_IFREG)) {
if (btrfs_test_opt(root, NODATASUM))
BTRFS_I(inode)->flags |= BTRFS_INODE_NODATASUM;
if (btrfs_test_opt(root, NODATACOW))
if (btrfs_test_opt(root, NODATACOW) ||
(BTRFS_I(dir)->flags & BTRFS_INODE_NODATACOW))
BTRFS_I(inode)->flags |= BTRFS_INODE_NODATACOW;
}

Expand Down Expand Up @@ -6866,6 +6869,26 @@ static int btrfs_getattr(struct vfsmount *mnt,
return 0;
}

/*
* If a file is moved, it will inherit the cow and compression flags of the new
* directory.
*/
static void fixup_inode_flags(struct inode *dir, struct inode *inode)
{
struct btrfs_inode *b_dir = BTRFS_I(dir);
struct btrfs_inode *b_inode = BTRFS_I(inode);

if (b_dir->flags & BTRFS_INODE_NODATACOW)
b_inode->flags |= BTRFS_INODE_NODATACOW;
else
b_inode->flags &= ~BTRFS_INODE_NODATACOW;

if (b_dir->flags & BTRFS_INODE_COMPRESS)
b_inode->flags |= BTRFS_INODE_COMPRESS;
else
b_inode->flags &= ~BTRFS_INODE_COMPRESS;
}

static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry)
{
Expand Down Expand Up @@ -6999,6 +7022,8 @@ static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry,
}
}

fixup_inode_flags(new_dir, old_inode);

ret = btrfs_add_link(trans, new_dir, old_inode,
new_dentry->d_name.name,
new_dentry->d_name.len, 0, index);
Expand Down
41 changes: 37 additions & 4 deletions fs/btrfs/ioctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,24 @@ static int btrfs_ioctl_getflags(struct file *file, void __user *arg)
return 0;
}

static int check_flags(unsigned int flags)
{
if (flags & ~(FS_IMMUTABLE_FL | FS_APPEND_FL | \
FS_NOATIME_FL | FS_NODUMP_FL | \
FS_SYNC_FL | FS_DIRSYNC_FL | \
FS_NOCOMP_FL | FS_COMPR_FL | \
FS_NOCOW_FL | FS_COW_FL))
return -EOPNOTSUPP;

if ((flags & FS_NOCOMP_FL) && (flags & FS_COMPR_FL))
return -EINVAL;

if ((flags & FS_NOCOW_FL) && (flags & FS_COW_FL))
return -EINVAL;

return 0;
}

static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
{
struct inode *inode = file->f_path.dentry->d_inode;
Expand All @@ -153,10 +171,9 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
if (copy_from_user(&flags, arg, sizeof(flags)))
return -EFAULT;

if (flags & ~(FS_IMMUTABLE_FL | FS_APPEND_FL | \
FS_NOATIME_FL | FS_NODUMP_FL | \
FS_SYNC_FL | FS_DIRSYNC_FL))
return -EOPNOTSUPP;
ret = check_flags(flags);
if (ret)
return ret;

if (!is_owner_or_cap(inode))
return -EACCES;
Expand Down Expand Up @@ -201,6 +218,22 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
else
ip->flags &= ~BTRFS_INODE_DIRSYNC;

/*
* The COMPRESS flag can only be changed by users, while the NOCOMPRESS
* flag may be changed automatically if compression code won't make
* things smaller.
*/
if (flags & FS_NOCOMP_FL) {
ip->flags &= ~BTRFS_INODE_COMPRESS;
ip->flags |= BTRFS_INODE_NOCOMPRESS;
} else if (flags & FS_COMPR_FL) {
ip->flags |= BTRFS_INODE_COMPRESS;
ip->flags &= ~BTRFS_INODE_NOCOMPRESS;
}
if (flags & FS_NOCOW_FL)
ip->flags |= BTRFS_INODE_NODATACOW;
else if (flags & FS_COW_FL)
ip->flags &= ~BTRFS_INODE_NODATACOW;

trans = btrfs_join_transaction(root, 1);
BUG_ON(IS_ERR(trans));
Expand Down

0 comments on commit 75e7cb7

Please sign in to comment.