Re: [PATCH 08/16 v2] f2fs: add file operations

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



2012/10/23 Jaegeuk Kim <jaegeuk.kim@xxxxxxxxxxx>:
> This adds memory operations and file/file_inode operations.
>
> - F2FS supports fallocate(), mmap(), fsync(), and basic ioctl().
>
> Signed-off-by: Jaegeuk Kim <jaegeuk.kim@xxxxxxxxxxx>
> ---
>  fs/f2fs/file.c |  640 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 640 insertions(+)
>  create mode 100644 fs/f2fs/file.c
>
> diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
> new file mode 100644
> index 0000000..81b1fd0
> --- /dev/null
> +++ b/fs/f2fs/file.c
> @@ -0,0 +1,640 @@
> +/**
> + * fs/f2fs/file.c
> + *
> + * Copyright (c) 2012 Samsung Electronics Co., Ltd.
> + *             http://www.samsung.com/
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +#include <linux/fs.h>
> +#include <linux/f2fs_fs.h>
> +#include <linux/stat.h>
> +#include <linux/buffer_head.h>
> +#include <linux/writeback.h>
> +#include <linux/falloc.h>
> +#include <linux/types.h>
> +#include <linux/uaccess.h>
> +#include <linux/mount.h>
> +
> +#include "f2fs.h"
> +#include "node.h"
> +#include "segment.h"
> +#include "xattr.h"
> +#include "acl.h"
> +
> +static int f2fs_vm_page_mkwrite(struct vm_area_struct *vma,
> +                                               struct vm_fault *vmf)
> +{
> +       struct page *page = vmf->page;
> +       struct inode *inode = vma->vm_file->f_path.dentry->d_inode;
> +       struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
> +       struct page *node_page;
> +       block_t old_blk_addr;
> +       struct dnode_of_data dn;
> +       int err;
> +
> +       f2fs_balance_fs(sbi);
> +
> +       sb_start_pagefault(inode->i_sb);
> +
> +       mutex_lock_op(sbi, DATA_NEW);
> +
> +       /* block allocation */
> +       set_new_dnode(&dn, inode, NULL, NULL, 0);
> +       err = get_dnode_of_data(&dn, page->index, 0);
> +       if (err) {
> +               mutex_unlock_op(sbi, DATA_NEW);
> +               goto out;
> +       }
> +
> +       old_blk_addr = dn.data_blkaddr;
> +       node_page = dn.node_page;
> +
> +       if (old_blk_addr == NULL_ADDR) {
> +               err = reserve_new_block(&dn);
> +               if (err) {
> +                       f2fs_put_dnode(&dn);
> +                       mutex_unlock_op(sbi, DATA_NEW);
> +                       goto out;
> +               }
> +       }
> +       f2fs_put_dnode(&dn);
> +
> +       mutex_unlock_op(sbi, DATA_NEW);
> +
> +       lock_page(page);
> +       if (page->mapping != inode->i_mapping ||
> +                       page_offset(page) >= i_size_read(inode) ||
> +                       !PageUptodate(page)) {
> +               unlock_page(page);
> +               err = -EFAULT;
> +               goto out;
> +       }
> +
> +       /*
> +        * check to see if the page is mapped already (no holes)
> +        */
> +       if (PageMappedToDisk(page))
> +               goto out;
> +
> +       /* fill the page */
> +       wait_on_page_writeback(page);
> +
> +       /* page is wholly or partially inside EOF */
> +       if (((page->index + 1) << PAGE_CACHE_SHIFT) > i_size_read(inode)) {
> +               unsigned offset;
> +               offset = i_size_read(inode) & ~PAGE_CACHE_MASK;
> +               zero_user_segment(page, offset, PAGE_CACHE_SIZE);
> +       }
> +       set_page_dirty(page);
> +       SetPageUptodate(page);
> +
> +       file_update_time(vma->vm_file);
> +out:
> +       sb_end_pagefault(inode->i_sb);
> +       return block_page_mkwrite_return(err);
> +}
> +
> +static const struct vm_operations_struct f2fs_file_vm_ops = {
> +       .fault        = filemap_fault,
> +       .page_mkwrite = f2fs_vm_page_mkwrite,
> +};
> +
> +static int need_to_sync_dir(struct f2fs_sb_info *sbi, struct inode *inode)
> +{
> +       struct dentry *dentry;
> +       nid_t pino;
> +
> +       inode = igrab(inode);
> +       dentry = d_find_any_alias(inode);
> +       if (!dentry) {
> +               iput(inode);
> +               return 0;
> +       }
> +       pino = dentry->d_parent->d_inode->i_ino;
> +       dput(dentry);
> +       iput(inode);
> +       return !is_checkpointed_node(sbi, pino);
> +}
> +
> +int f2fs_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
> +{
> +       struct inode *inode = file->f_mapping->host;
> +       struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
> +       unsigned long long cur_version;
> +       int ret = 0;
> +       bool need_cp = false;
> +       struct writeback_control wbc = {
> +               .sync_mode = WB_SYNC_ALL,
> +               .nr_to_write = LONG_MAX,
> +               .for_reclaim = 0,
> +       };
> +
> +       ret = filemap_write_and_wait_range(inode->i_mapping, start, end);
> +       if (ret)
> +               return ret;
> +
> +       mutex_lock(&inode->i_mutex);
> +
> +       if (inode->i_sb->s_flags & MS_RDONLY)
> +               goto out;
> +       if (datasync && !(inode->i_state & I_DIRTY_DATASYNC))
> +               goto out;
> +
> +       mutex_lock(&sbi->cp_mutex);
> +       cur_version = le64_to_cpu(F2FS_CKPT(sbi)->checkpoint_ver);
> +       mutex_unlock(&sbi->cp_mutex);
> +
> +       if (F2FS_I(inode)->data_version != cur_version &&
> +                                       !(inode->i_state & I_DIRTY))
> +               goto out;
> +       F2FS_I(inode)->data_version--;
> +
> +       if (!S_ISREG(inode->i_mode) || inode->i_nlink != 1)
> +               need_cp = true;
> +       if (is_inode_flag_set(F2FS_I(inode), FI_NEED_CP))
> +               need_cp = true;
> +       if (!space_for_roll_forward(sbi))
> +               need_cp = true;
> +       if (need_to_sync_dir(sbi, inode))
> +               need_cp = true;
> +
> +       f2fs_write_inode(inode, NULL);
> +
> +       if (need_cp) {
> +               /* all the dirty node pages should be flushed for POR */
> +               ret = f2fs_sync_fs(inode->i_sb, 1);
> +               clear_inode_flag(F2FS_I(inode), FI_NEED_CP);
> +       } else {
> +               while (sync_node_pages(sbi, inode->i_ino, &wbc) == 0)
> +                       f2fs_write_inode(inode, NULL);
> +               filemap_fdatawait_range(sbi->node_inode->i_mapping,
> +                                                       0, LONG_MAX);
> +       }
> +out:
> +       mutex_unlock(&inode->i_mutex);
> +       return ret;
> +}
> +
> +static int f2fs_file_mmap(struct file *file, struct vm_area_struct *vma)
> +{
> +       file_accessed(file);
> +       vma->vm_ops = &f2fs_file_vm_ops;
> +       return 0;
> +}
> +
> +static int truncate_data_blocks_range(struct dnode_of_data *dn, int count)
> +{
> +       int nr_free = 0, ofs = dn->ofs_in_node;
> +       struct f2fs_sb_info *sbi = F2FS_SB(dn->inode->i_sb);
> +       struct f2fs_node *raw_node;
> +       __le32 *addr;
> +
> +       raw_node = page_address(dn->node_page);
> +       addr = blkaddr_in_node(raw_node) + ofs;
> +
> +       for ( ; count > 0; count--, addr++, dn->ofs_in_node++) {
> +               block_t blkaddr = le32_to_cpu(*addr);
> +               if (blkaddr == NULL_ADDR)
> +                       continue;
> +
> +               update_extent_cache(NULL_ADDR, dn);
> +               invalidate_blocks(sbi, blkaddr);
> +               dec_valid_block_count(sbi, dn->inode, 1);
> +               nr_free++;
> +       }
> +       if (nr_free) {
> +               set_page_dirty(dn->node_page);
> +               sync_inode_page(dn);
> +       }
> +       dn->ofs_in_node = ofs;
> +       return nr_free;
> +}
> +
> +void truncate_data_blocks(struct dnode_of_data *dn)
> +{
> +       truncate_data_blocks_range(dn, ADDRS_PER_BLOCK);
> +}
> +
> +static void truncate_partial_data_page(struct inode *inode, u64 from)
> +{
> +       unsigned offset = from & (PAGE_CACHE_SIZE - 1);
> +       struct page *page;
> +
> +       if (!offset)
> +               return;
> +
> +       page = find_data_page(inode, from >> PAGE_CACHE_SHIFT);
> +       if (IS_ERR(page))
> +               return;
> +
> +       lock_page(page);
> +       wait_on_page_writeback(page);
> +       zero_user(page, offset, PAGE_CACHE_SIZE - offset);
> +       set_page_dirty(page);
> +       f2fs_put_page(page, 1);
> +}
> +
> +static int truncate_blocks(struct inode *inode, u64 from)
> +{
> +       struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
> +       unsigned int blocksize = inode->i_sb->s_blocksize;
> +       struct dnode_of_data dn;
> +       pgoff_t free_from;
> +       int count = 0;
> +       int err;
> +
> +       free_from = (pgoff_t)
> +                       ((from + blocksize - 1) >> (sbi->log_blocksize));
> +
> +       mutex_lock_op(sbi, DATA_TRUNC);
> +
> +       set_new_dnode(&dn, inode, NULL, NULL, 0);
> +       err = get_dnode_of_data(&dn, free_from, RDONLY_NODE);
> +       if (err) {
> +               if (err == -ENOENT)
> +                       goto free_next;
> +               mutex_unlock_op(sbi, DATA_TRUNC);
> +               return err;
> +       }
> +
> +       if (IS_INODE(dn.node_page))
> +               count = ADDRS_PER_INODE;
> +       else
> +               count = ADDRS_PER_BLOCK;
> +
> +       count -= dn.ofs_in_node;
> +       BUG_ON(count < 0);
> +       if (dn.ofs_in_node || IS_INODE(dn.node_page)) {
> +               truncate_data_blocks_range(&dn, count);
> +               free_from += count;
> +       }
> +
> +       f2fs_put_dnode(&dn);
> +free_next:
> +       err = truncate_inode_blocks(inode, free_from);
> +       mutex_unlock_op(sbi, DATA_TRUNC);
> +
> +       /* lastly zero out the first data page */
> +       truncate_partial_data_page(inode, from);
> +
> +       return err;
> +}
> +
> +void f2fs_truncate(struct inode *inode)
> +{
> +       if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
> +                               S_ISLNK(inode->i_mode)))
> +               return;
> +
> +       if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
> +               return;

No truncate for an append only file? You call f2fs_truncate from
evict_inode, so no block freeing when this kind of inode is deleted.

> +
> +       if (!truncate_blocks(inode, i_size_read(inode))) {
> +               inode->i_mtime = inode->i_ctime = CURRENT_TIME;
> +               mark_inode_dirty(inode);
> +       }
> +
> +       f2fs_balance_fs(F2FS_SB(inode->i_sb));
> +}
> +
> +static int f2fs_getattr(struct vfsmount *mnt,
> +                        struct dentry *dentry, struct kstat *stat)
> +{
> +       struct inode *inode = dentry->d_inode;
> +       generic_fillattr(inode, stat);
> +       stat->blocks <<= 3;
> +       return 0;
> +}
> +
> +#ifdef CONFIG_F2FS_FS_POSIX_ACL
> +static void __setattr_copy(struct inode *inode, const struct iattr *attr)
> +{
> +       struct f2fs_inode_info *fi = F2FS_I(inode);
> +       unsigned int ia_valid = attr->ia_valid;
> +
> +       if (ia_valid & ATTR_UID)
> +               inode->i_uid = attr->ia_uid;
> +       if (ia_valid & ATTR_GID)
> +               inode->i_gid = attr->ia_gid;
> +       if (ia_valid & ATTR_ATIME)
> +               inode->i_atime = timespec_trunc(attr->ia_atime,
> +                                               inode->i_sb->s_time_gran);
> +       if (ia_valid & ATTR_MTIME)
> +               inode->i_mtime = timespec_trunc(attr->ia_mtime,
> +                                               inode->i_sb->s_time_gran);
> +       if (ia_valid & ATTR_CTIME)
> +               inode->i_ctime = timespec_trunc(attr->ia_ctime,
> +                                               inode->i_sb->s_time_gran);
> +       if (ia_valid & ATTR_MODE) {
> +               umode_t mode = attr->ia_mode;
> +
> +               if (!in_group_p(inode->i_gid) && !capable(CAP_FSETID))
> +                       mode &= ~S_ISGID;
> +               set_acl_inode(fi, mode);
> +       }
> +}
> +#else
> +#define __setattr_copy setattr_copy
> +#endif
> +
> +int f2fs_setattr(struct dentry *dentry, struct iattr *attr)
> +{
> +       struct inode *inode = dentry->d_inode;
> +       struct f2fs_inode_info *fi = F2FS_I(inode);
> +       int err;
> +
> +       err = inode_change_ok(inode, attr);
> +       if (err)
> +               return err;
> +
> +       if ((attr->ia_valid & ATTR_SIZE) &&
> +                       attr->ia_size != i_size_read(inode)) {
> +               truncate_setsize(inode, attr->ia_size);
> +               f2fs_truncate(inode);

No need to call truncate_pagecache & co.?

Marco
--
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Ext4 Filesystem]     [Union Filesystem]     [Filesystem Testing]     [Ceph Users]     [Ecryptfs]     [AutoFS]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux Cachefs]     [Reiser Filesystem]     [Linux RAID]     [Samba]     [Device Mapper]     [CEPH Development]
  Powered by Linux