cregit-Linux how code gets into the kernel

Release 4.10 fs/f2fs/file.c

Directory: fs/f2fs
/*
 * 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/blkdev.h>
#include <linux/falloc.h>
#include <linux/types.h>
#include <linux/compat.h>
#include <linux/uaccess.h>
#include <linux/mount.h>
#include <linux/pagevec.h>
#include <linux/uuid.h>
#include <linux/file.h>

#include "f2fs.h"
#include "node.h"
#include "segment.h"
#include "xattr.h"
#include "acl.h"
#include "gc.h"
#include "trace.h"
#include <trace/events/f2fs.h>


static int f2fs_vm_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) { struct page *page = vmf->page; struct inode *inode = file_inode(vma->vm_file); struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct dnode_of_data dn; int err; sb_start_pagefault(inode->i_sb); f2fs_bug_on(sbi, f2fs_has_inline_data(inode)); /* block allocation */ f2fs_lock_op(sbi); set_new_dnode(&dn, inode, NULL, NULL, 0); err = f2fs_reserve_block(&dn, page->index); if (err) { f2fs_unlock_op(sbi); goto out; } f2fs_put_dnode(&dn); f2fs_unlock_op(sbi); f2fs_balance_fs(sbi, dn.node_changed); file_update_time(vma->vm_file); lock_page(page); if (unlikely(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 mapped; /* page is wholly or partially inside EOF */ if (((loff_t)(page->index + 1) << PAGE_SHIFT) > i_size_read(inode)) { unsigned offset; offset = i_size_read(inode) & ~PAGE_MASK; zero_user_segment(page, offset, PAGE_SIZE); } set_page_dirty(page); if (!PageUptodate(page)) SetPageUptodate(page); trace_f2fs_vm_page_mkwrite(page, DATA); mapped: /* fill the page */ f2fs_wait_on_page_writeback(page, DATA, false); /* wait for GCed encrypted page writeback */ if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) f2fs_wait_on_encrypted_page_writeback(sbi, dn.data_blkaddr); out: sb_end_pagefault(inode->i_sb); f2fs_update_time(sbi, REQ_TIME); return block_page_mkwrite_return(err); }

Contributors

PersonTokensPropCommitsCommitProp
jaegeuk kimjaegeuk kim28485.03%1263.16%
chao yuchao yu298.68%210.53%
namjae jeonnamjae jeon133.89%15.26%
al viroal viro30.90%15.26%
kirill a. shutemovkirill a. shutemov30.90%15.26%
gu zhenggu zheng10.30%15.26%
huajun lihuajun li10.30%15.26%
Total334100.00%19100.00%

static const struct vm_operations_struct f2fs_file_vm_ops = { .fault = filemap_fault, .map_pages = filemap_map_pages, .page_mkwrite = f2fs_vm_page_mkwrite, };
static int get_parent_ino(struct inode *inode, nid_t *pino) { struct dentry *dentry; inode = igrab(inode); dentry = d_find_any_alias(inode); iput(inode); if (!dentry) return 0; if (update_dent_inode(inode, inode, &dentry->d_name)) { dput(dentry); return 0; } *pino = parent_ino(dentry); dput(dentry); return 1; }

Contributors

PersonTokensPropCommitsCommitProp
jaegeuk kimjaegeuk kim87100.00%3100.00%
Total87100.00%3100.00%


static inline bool need_do_checkpoint(struct inode *inode) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); bool need_cp = false; if (!S_ISREG(inode->i_mode) || inode->i_nlink != 1) need_cp = true; else if (is_sbi_flag_set(sbi, SBI_NEED_CP)) need_cp = true; else if (file_wrong_pino(inode)) need_cp = true; else if (!space_for_roll_forward(sbi)) need_cp = true; else if (!is_checkpointed_node(sbi, F2FS_I(inode)->i_pino)) need_cp = true; else if (F2FS_I(inode)->xattr_ver == cur_cp_version(F2FS_CKPT(sbi))) need_cp = true; else if (test_opt(sbi, FASTBOOT)) need_cp = true; else if (sbi->active_logs == 2) need_cp = true; return need_cp; }

Contributors

PersonTokensPropCommitsCommitProp
chao yuchao yu11673.42%116.67%
jaegeuk kimjaegeuk kim4226.58%583.33%
Total158100.00%6100.00%


static bool need_inode_page_update(struct f2fs_sb_info *sbi, nid_t ino) { struct page *i = find_get_page(NODE_MAPPING(sbi), ino); bool ret = false; /* But we need to avoid that there are some inode updates */ if ((i && PageDirty(i)) || need_inode_block_update(sbi, ino)) ret = true; f2fs_put_page(i, 0); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
changman leechangman lee67100.00%1100.00%
Total67100.00%1100.00%


static void try_to_fix_pino(struct inode *inode) { struct f2fs_inode_info *fi = F2FS_I(inode); nid_t pino; down_write(&fi->i_sem); fi->xattr_ver = 0; if (file_wrong_pino(inode) && inode->i_nlink == 1 && get_parent_ino(inode, &pino)) { f2fs_i_pino_write(inode, pino); file_got_pino(inode); } up_write(&fi->i_sem); }

Contributors

PersonTokensPropCommitsCommitProp
changman leechangman lee7693.83%150.00%
jaegeuk kimjaegeuk kim56.17%150.00%
Total81100.00%2100.00%


static int f2fs_do_sync_file(struct file *file, loff_t start, loff_t end, int datasync, bool atomic) { struct inode *inode = file->f_mapping->host; struct f2fs_sb_info *sbi = F2FS_I_SB(inode); nid_t ino = inode->i_ino; int ret = 0; bool need_cp = false; struct writeback_control wbc = { .sync_mode = WB_SYNC_ALL, .nr_to_write = LONG_MAX, .for_reclaim = 0, }; if (unlikely(f2fs_readonly(inode->i_sb))) return 0; trace_f2fs_sync_file_enter(inode); /* if fdatasync is triggered, let's do in-place-update */ if (datasync || get_dirty_pages(inode) <= SM_I(sbi)->min_fsync_blocks) set_inode_flag(inode, FI_NEED_IPU); ret = filemap_write_and_wait_range(inode->i_mapping, start, end); clear_inode_flag(inode, FI_NEED_IPU); if (ret) { trace_f2fs_sync_file_exit(inode, need_cp, datasync, ret); return ret; } /* if the inode is dirty, let's recover all the time */ if (!f2fs_skip_inode_update(inode, datasync)) { f2fs_write_inode(inode, NULL); goto go_write; } /* * if there is no written data, don't waste time to write recovery info. */ if (!is_inode_flag_set(inode, FI_APPEND_WRITE) && !exist_written_data(sbi, ino, APPEND_INO)) { /* it may call write_inode just prior to fsync */ if (need_inode_page_update(sbi, ino)) goto go_write; if (is_inode_flag_set(inode, FI_UPDATE_WRITE) || exist_written_data(sbi, ino, UPDATE_INO)) goto flush_out; goto out; } go_write: /* * Both of fdatasync() and fsync() are able to be recovered from * sudden-power-off. */ down_read(&F2FS_I(inode)->i_sem); need_cp = need_do_checkpoint(inode); up_read(&F2FS_I(inode)->i_sem); if (need_cp) { /* all the dirty node pages should be flushed for POR */ ret = f2fs_sync_fs(inode->i_sb, 1); /* * We've secured consistency through sync_fs. Following pino * will be used only for fsynced inodes after checkpoint. */ try_to_fix_pino(inode); clear_inode_flag(inode, FI_APPEND_WRITE); clear_inode_flag(inode, FI_UPDATE_WRITE); goto out; } sync_nodes: ret = fsync_node_pages(sbi, inode, &wbc, atomic); if (ret) goto out; /* if cp_error was enabled, we should avoid infinite loop */ if (unlikely(f2fs_cp_error(sbi))) { ret = -EIO; goto out; } if (need_inode_block_update(sbi, ino)) { f2fs_mark_inode_dirty_sync(inode, true); f2fs_write_inode(inode, NULL); goto sync_nodes; } ret = wait_on_node_pages_writeback(sbi, ino); if (ret) goto out; /* once recovery info is written, don't need to tack this */ remove_ino_entry(sbi, ino, APPEND_INO); clear_inode_flag(inode, FI_APPEND_WRITE); flush_out: remove_ino_entry(sbi, ino, UPDATE_INO); clear_inode_flag(inode, FI_UPDATE_WRITE); ret = f2fs_issue_flush(sbi); f2fs_update_time(sbi, REQ_TIME); out: trace_f2fs_sync_file_exit(inode, need_cp, datasync, ret); f2fs_trace_ios(NULL, 1); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
jaegeuk kimjaegeuk kim41285.48%3378.57%
namjae jeonnamjae jeon387.88%24.76%
changman leechangman lee193.94%37.14%
chao yuchao yu132.70%49.52%
Total482100.00%42100.00%


int f2fs_sync_file(struct file *file, loff_t start, loff_t end, int datasync) { return f2fs_do_sync_file(file, start, end, datasync, false); }

Contributors

PersonTokensPropCommitsCommitProp
jaegeuk kimjaegeuk kim33100.00%1100.00%
Total33100.00%1100.00%


static pgoff_t __get_first_dirty_index(struct address_space *mapping, pgoff_t pgofs, int whence) { struct pagevec pvec; int nr_pages; if (whence != SEEK_DATA) return 0; /* find first dirty page index */ pagevec_init(&pvec, 0); nr_pages = pagevec_lookup_tag(&pvec, mapping, &pgofs, PAGECACHE_TAG_DIRTY, 1); pgofs = nr_pages ? pvec.pages[0]->index : ULONG_MAX; pagevec_release(&pvec); return pgofs; }

Contributors

PersonTokensPropCommitsCommitProp
jaegeuk kimjaegeuk kim8298.80%150.00%
chao yuchao yu11.20%150.00%
Total83100.00%2100.00%


static bool __found_offset(block_t blkaddr, pgoff_t dirty, pgoff_t pgofs, int whence) { switch (whence) { case SEEK_DATA: if ((blkaddr == NEW_ADDR && dirty == pgofs) || (blkaddr != NEW_ADDR && blkaddr != NULL_ADDR)) return true; break; case SEEK_HOLE: if (blkaddr == NULL_ADDR) return true; break; } return false; }

Contributors

PersonTokensPropCommitsCommitProp
jaegeuk kimjaegeuk kim69100.00%1100.00%
Total69100.00%1100.00%


static loff_t f2fs_seek_block(struct file *file, loff_t offset, int whence) { struct inode *inode = file->f_mapping->host; loff_t maxbytes = inode->i_sb->s_maxbytes; struct dnode_of_data dn; pgoff_t pgofs, end_offset, dirty; loff_t data_ofs = offset; loff_t isize; int err = 0; inode_lock(inode); isize = i_size_read(inode); if (offset >= isize) goto fail; /* handle inline data case */ if (f2fs_has_inline_data(inode) || f2fs_has_inline_dentry(inode)) { if (whence == SEEK_HOLE) data_ofs = isize; goto found; } pgofs = (pgoff_t)(offset >> PAGE_SHIFT); dirty = __get_first_dirty_index(inode->i_mapping, pgofs, whence); for (; data_ofs < isize; data_ofs = (loff_t)pgofs << PAGE_SHIFT) { set_new_dnode(&dn, inode, NULL, NULL, 0); err = get_dnode_of_data(&dn, pgofs, LOOKUP_NODE); if (err && err != -ENOENT) { goto fail; } else if (err == -ENOENT) { /* direct node does not exists */ if (whence == SEEK_DATA) { pgofs = get_next_page_offset(&dn, pgofs); continue; } else { goto found; } } end_offset = ADDRS_PER_PAGE(dn.node_page, inode); /* find data/hole in dnode block */ for (; dn.ofs_in_node < end_offset; dn.ofs_in_node++, pgofs++, data_ofs = (loff_t)pgofs << PAGE_SHIFT) { block_t blkaddr; blkaddr = datablock_addr(dn.node_page, dn.ofs_in_node); if (__found_offset(blkaddr, dirty, pgofs, whence)) { f2fs_put_dnode(&dn); goto found; } } f2fs_put_dnode(&dn); } if (whence == SEEK_DATA) goto fail; found: if (whence == SEEK_HOLE && data_ofs > isize) data_ofs = isize; inode_unlock(inode); return vfs_setpos(file, data_ofs, maxbytes); fail: inode_unlock(inode); return -ENXIO; }

Contributors

PersonTokensPropCommitsCommitProp
chao yuchao yu32087.67%650.00%
jaegeuk kimjaegeuk kim3810.41%325.00%
kirill a. shutemovkirill a. shutemov30.82%18.33%
al viroal viro30.82%18.33%
arter 97arter 9710.27%18.33%
Total365100.00%12100.00%


static loff_t f2fs_llseek(struct file *file, loff_t offset, int whence) { struct inode *inode = file->f_mapping->host; loff_t maxbytes = inode->i_sb->s_maxbytes; switch (whence) { case SEEK_SET: case SEEK_CUR: case SEEK_END: return generic_file_llseek_size(file, offset, whence, maxbytes, i_size_read(inode)); case SEEK_DATA: case SEEK_HOLE: if (offset < 0) return -ENXIO; return f2fs_seek_block(file, offset, whence); } return -EINVAL; }

Contributors

PersonTokensPropCommitsCommitProp
chao yuchao yu8989.90%150.00%
jaegeuk kimjaegeuk kim1010.10%150.00%
Total99100.00%2100.00%


static int f2fs_file_mmap(struct file *file, struct vm_area_struct *vma) { struct inode *inode = file_inode(file); int err; if (f2fs_encrypted_inode(inode)) { err = fscrypt_get_encryption_info(inode); if (err) return 0; if (!f2fs_encrypted_inode(inode)) return -ENOKEY; } /* we don't need to use inline_data strictly */ err = f2fs_convert_inline_inode(inode); if (err) return err; file_accessed(file); vma->vm_ops = &f2fs_file_vm_ops; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
jaegeuk kimjaegeuk kim8287.23%583.33%
chao yuchao yu1212.77%116.67%
Total94100.00%6100.00%


static int f2fs_file_open(struct inode *inode, struct file *filp) { int ret = generic_file_open(inode, filp); struct dentry *dir; if (!ret && f2fs_encrypted_inode(inode)) { ret = fscrypt_get_encryption_info(inode); if (ret) return -EACCES; if (!fscrypt_has_encryption_key(inode)) return -ENOKEY; } dir = dget_parent(file_dentry(filp)); if (f2fs_encrypted_inode(d_inode(dir)) && !fscrypt_has_permitted_context(d_inode(dir), inode)) { dput(dir); return -EPERM; } dput(dir); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
jaegeuk kimjaegeuk kim10789.17%480.00%
chao yuchao yu1310.83%120.00%
Total120100.00%5100.00%


int truncate_data_blocks_range(struct dnode_of_data *dn, int count) { struct f2fs_sb_info *sbi = F2FS_I_SB(dn->inode); struct f2fs_node *raw_node; int nr_free = 0, ofs = dn->ofs_in_node, len = count; __le32 *addr; raw_node = F2FS_NODE(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; dn->data_blkaddr = NULL_ADDR; set_data_blkaddr(dn); invalidate_blocks(sbi, blkaddr); if (dn->ofs_in_node == 0 && IS_INODE(dn->node_page)) clear_inode_flag(dn->inode, FI_FIRST_BLOCK_WRITTEN); nr_free++; } if (nr_free) { pgoff_t fofs; /* * once we invalidate valid blkaddr in range [ofs, ofs + count], * we will invalidate all blkaddr in the whole range. */ fofs = start_bidx_of_node(ofs_of_node(dn->node_page), dn->inode) + ofs; f2fs_update_extent_cache_range(dn, fofs, 0, len); dec_valid_block_count(sbi, dn->inode, nr_free); } dn->ofs_in_node = ofs; f2fs_update_time(sbi, REQ_TIME); trace_f2fs_truncate_data_blocks_range(dn->inode, dn->nid, dn->ofs_in_node, nr_free); return nr_free; }

Contributors

PersonTokensPropCommitsCommitProp
jaegeuk kimjaegeuk kim15064.94%550.00%
chao yuchao yu5322.94%220.00%
namjae jeonnamjae jeon2711.69%220.00%
gu zhenggu zheng10.43%110.00%
Total231100.00%10100.00%


void truncate_data_blocks(struct dnode_of_data *dn) { truncate_data_blocks_range(dn, ADDRS_PER_BLOCK); }

Contributors

PersonTokensPropCommitsCommitProp
jaegeuk kimjaegeuk kim17100.00%1100.00%
Total17100.00%1100.00%


static int truncate_partial_data_page(struct inode *inode, u64 from, bool cache_only) { unsigned offset = from & (PAGE_SIZE - 1); pgoff_t index = from >> PAGE_SHIFT; struct address_space *mapping = inode->i_mapping; struct page *page; if (!offset && !cache_only) return 0; if (cache_only) { page = find_lock_page(mapping, index); if (page && PageUptodate(page)) goto truncate_out; f2fs_put_page(page, 1); return 0; } page = get_lock_data_page(inode, index, true); if (IS_ERR(page)) return 0; truncate_out: f2fs_wait_on_page_writeback(page, DATA, true); zero_user(page, offset, PAGE_SIZE - offset); if (!cache_only || !f2fs_encrypted_inode(inode) || !S_ISREG(inode->i_mode)) set_page_dirty(page); f2fs_put_page(page, 1); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
jaegeuk kimjaegeuk kim16091.95%1076.92%
chao yuchao yu116.32%215.38%
kirill a. shutemovkirill a. shutemov31.72%17.69%
Total174100.00%13100.00%


int truncate_blocks(struct inode *inode, u64 from, bool lock) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); unsigned int blocksize = inode->i_sb->s_blocksize; struct dnode_of_data dn; pgoff_t free_from; int count = 0, err = 0; struct page *ipage; bool truncate_page = false; trace_f2fs_truncate_blocks_enter(inode, from); free_from = (pgoff_t)F2FS_BYTES_TO_BLK(from + blocksize - 1); if (free_from >= sbi->max_file_blocks) goto free_partial; if (lock) f2fs_lock_op(sbi); ipage = get_node_page(sbi, inode->i_ino); if (IS_ERR(ipage)) { err = PTR_ERR(ipage); goto out; } if (f2fs_has_inline_data(inode)) { if (truncate_inline_inode(ipage, from)) set_page_dirty(ipage); f2fs_put_page(ipage, 1); truncate_page = true; goto out; } set_new_dnode(&dn, inode, ipage, NULL, 0); err = get_dnode_of_data(&dn, free_from, LOOKUP_NODE_RA); if (err) { if (err == -ENOENT) goto free_next; goto out; } count = ADDRS_PER_PAGE(dn.node_page, inode); count -= dn.ofs_in_node; f2fs_bug_on(sbi, 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); out: if (lock) f2fs_unlock_op(sbi); free_partial: /* lastly zero out the first data page */ if (!err) err = truncate_partial_data_page(inode, from, truncate_page); trace_f2fs_truncate_blocks_exit(inode, err); return err; }

Contributors

PersonTokensPropCommitsCommitProp
jaegeuk kimjaegeuk kim26581.54%1164.71%
chao yuchao yu4212.92%317.65%
namjae jeonnamjae jeon144.31%15.88%
huajun lihuajun li30.92%15.88%
gu zhenggu zheng10.31%15.88%
Total325100.00%17100.00%


int f2fs_truncate(struct inode *inode) { int err; if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))) return 0; trace_f2fs_truncate(inode); /* we should check inline_data size */ if (!f2fs_may_inline_data(inode)) { err = f2fs_convert_inline_inode(inode); if (err) return err; }