Skip to content

Commit

Permalink
f2fs: add F2FS_IOC_DECOMPRESS_FILE and F2FS_IOC_COMPRESS_FILE
Browse files Browse the repository at this point in the history
Added two ioctl to decompress/compress explicitly the compression
enabled file in "compress_mode=user" mount option.

Using these two ioctls, the users can make a control of compression
and decompression of their files.

Signed-off-by: Daeho Jeong <daehojeong@google.com>
  • Loading branch information
Daeho Jeong authored and intel-lab-lkp committed Dec 3, 2020
1 parent 0eedcea commit c7c9bfc
Show file tree
Hide file tree
Showing 2 changed files with 187 additions and 0 deletions.
185 changes: 185 additions & 0 deletions fs/f2fs/file.c
Expand Up @@ -4026,6 +4026,185 @@ static int f2fs_ioc_set_compress_option(struct file *filp, unsigned long arg)
return ret;
}

static int redirty_blocks(struct inode *inode, pgoff_t page_idx, int len)
{
DEFINE_READAHEAD(ractl, NULL, inode->i_mapping, page_idx);
struct address_space *mapping = inode->i_mapping;
struct page *page;
pgoff_t redirty_idx = page_idx;
int i, page_len = 0, ret = 0;

page_cache_ra_unbounded(&ractl, len, 0);

for (i = 0; i < len; i++, page_idx++) {
page = read_cache_page(mapping, page_idx, NULL, NULL);
if (IS_ERR(page)) {
ret = PTR_ERR(page);
break;
}
page_len++;
}

for (i = 0; i < page_len; i++, redirty_idx++) {
page = find_lock_page(mapping, redirty_idx);
if (!page)
ret = -ENOENT;
set_page_dirty(page);
f2fs_put_page(page, 1);
f2fs_put_page(page, 0);
}

return ret;
}

static int f2fs_ioc_decompress_file(struct file *filp, unsigned long arg)
{
struct inode *inode = file_inode(filp);
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
struct f2fs_inode_info *fi = F2FS_I(inode);
pgoff_t page_idx = 0, last_idx;
unsigned int blk_per_seg = sbi->blocks_per_seg;
int cluster_size = F2FS_I(inode)->i_cluster_size;
int count, ret;

if (!f2fs_sb_has_compression(sbi) ||
F2FS_OPTION(sbi).compress_mode != COMPR_MODE_USER)
return -EOPNOTSUPP;

if (!(filp->f_mode & FMODE_WRITE))
return -EBADF;

if (!f2fs_compressed_file(inode))
return -EINVAL;

f2fs_balance_fs(F2FS_I_SB(inode), true);

file_start_write(filp);
inode_lock(inode);

if (!f2fs_is_compress_backend_ready(inode)) {
ret = -EOPNOTSUPP;
goto out;
}

if (f2fs_is_mmap_file(inode)) {
ret = -EBUSY;
goto out;
}

ret = filemap_write_and_wait_range(inode->i_mapping, 0, LLONG_MAX);
if (ret)
goto out;

if (!atomic_read(&fi->i_compr_blocks))
goto out;

last_idx = DIV_ROUND_UP(i_size_read(inode), PAGE_SIZE);

count = last_idx - page_idx;
while (count) {
int len = min(cluster_size, count);

ret = redirty_blocks(inode, page_idx, len);
if (ret < 0)
break;

if (get_dirty_pages(inode) >= blk_per_seg)
filemap_fdatawrite(inode->i_mapping);

count -= len;
page_idx += len;
}

if (!ret)
ret = filemap_write_and_wait_range(inode->i_mapping, 0,
LLONG_MAX);

if (ret)
f2fs_warn(sbi, "%s: The file might be partially decompressed "
"(errno=%d). Please delete the file.\n",
__func__, ret);
out:
inode_unlock(inode);
file_end_write(filp);

return ret;
}

static int f2fs_ioc_compress_file(struct file *filp, unsigned long arg)
{
struct inode *inode = file_inode(filp);
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
pgoff_t page_idx = 0, last_idx;
unsigned int blk_per_seg = sbi->blocks_per_seg;
int cluster_size = F2FS_I(inode)->i_cluster_size;
int count, ret;

if (!f2fs_sb_has_compression(sbi) ||
F2FS_OPTION(sbi).compress_mode != COMPR_MODE_USER)
return -EOPNOTSUPP;

if (!(filp->f_mode & FMODE_WRITE))
return -EBADF;

if (!f2fs_compressed_file(inode))
return -EINVAL;

f2fs_balance_fs(F2FS_I_SB(inode), true);

file_start_write(filp);
inode_lock(inode);

if (!f2fs_is_compress_backend_ready(inode)) {
ret = -EOPNOTSUPP;
goto out;
}

if (f2fs_is_mmap_file(inode)) {
ret = -EBUSY;
goto out;
}

ret = filemap_write_and_wait_range(inode->i_mapping, 0, LLONG_MAX);
if (ret)
goto out;

set_inode_flag(inode, FI_ENABLE_COMPRESS);

last_idx = DIV_ROUND_UP(i_size_read(inode), PAGE_SIZE);

count = last_idx - page_idx;
while (count) {
int len = min(cluster_size, count);

ret = redirty_blocks(inode, page_idx, len);
if (ret < 0)
break;

if (get_dirty_pages(inode) >= blk_per_seg)
filemap_fdatawrite(inode->i_mapping);

count -= len;
page_idx += len;
}

if (!ret)
ret = filemap_write_and_wait_range(inode->i_mapping, 0,
LLONG_MAX);

clear_inode_flag(inode, FI_ENABLE_COMPRESS);

if (ret)
f2fs_warn(sbi, "%s: The file might be partially compressed "
"(errno=%d). Please delete the file.\n",
__func__, ret);
out:
inode_unlock(inode);
file_end_write(filp);

return ret;
}

static long __f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
switch (cmd) {
Expand Down Expand Up @@ -4113,6 +4292,10 @@ static long __f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
return f2fs_ioc_get_compress_option(filp, arg);
case F2FS_IOC_SET_COMPRESS_OPTION:
return f2fs_ioc_set_compress_option(filp, arg);
case F2FS_IOC_DECOMPRESS_FILE:
return f2fs_ioc_decompress_file(filp, arg);
case F2FS_IOC_COMPRESS_FILE:
return f2fs_ioc_compress_file(filp, arg);
default:
return -ENOTTY;
}
Expand Down Expand Up @@ -4352,6 +4535,8 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
case F2FS_IOC_SEC_TRIM_FILE:
case F2FS_IOC_GET_COMPRESS_OPTION:
case F2FS_IOC_SET_COMPRESS_OPTION:
case F2FS_IOC_DECOMPRESS_FILE:
case F2FS_IOC_COMPRESS_FILE:
break;
default:
return -ENOIOCTLCMD;
Expand Down
2 changes: 2 additions & 0 deletions include/uapi/linux/f2fs.h
Expand Up @@ -40,6 +40,8 @@
struct f2fs_comp_option)
#define F2FS_IOC_SET_COMPRESS_OPTION _IOW(F2FS_IOCTL_MAGIC, 22, \
struct f2fs_comp_option)
#define F2FS_IOC_DECOMPRESS_FILE _IO(F2FS_IOCTL_MAGIC, 23)
#define F2FS_IOC_COMPRESS_FILE _IO(F2FS_IOCTL_MAGIC, 24)

/*
* should be same as XFS_IOC_GOINGDOWN.
Expand Down

0 comments on commit c7c9bfc

Please sign in to comment.