123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250 |
- /*
- * fs/f2fs/inline.c
- * Copyright (c) 2013, Intel Corporation
- * Authors: Huajun Li <huajun.li@intel.com>
- * Haicheng Li <haicheng.li@intel.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 "f2fs.h"
- bool f2fs_may_inline(struct inode *inode)
- {
- struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
- block_t nr_blocks;
- loff_t i_size;
- if (!test_opt(sbi, INLINE_DATA))
- return false;
- nr_blocks = F2FS_I(inode)->i_xattr_nid ? 3 : 2;
- if (inode->i_blocks > nr_blocks)
- return false;
- i_size = i_size_read(inode);
- if (i_size > MAX_INLINE_DATA)
- return false;
- return true;
- }
- int f2fs_read_inline_data(struct inode *inode, struct page *page)
- {
- struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
- struct page *ipage;
- void *src_addr, *dst_addr;
- if (page->index) {
- zero_user_segment(page, 0, PAGE_CACHE_SIZE);
- goto out;
- }
- ipage = get_node_page(sbi, inode->i_ino);
- if (IS_ERR(ipage)) {
- unlock_page(page);
- return PTR_ERR(ipage);
- }
- zero_user_segment(page, MAX_INLINE_DATA, PAGE_CACHE_SIZE);
- /* Copy the whole inline data block */
- src_addr = inline_data_addr(ipage);
- dst_addr = kmap(page);
- memcpy(dst_addr, src_addr, MAX_INLINE_DATA);
- kunmap(page);
- f2fs_put_page(ipage, 1);
- out:
- SetPageUptodate(page);
- unlock_page(page);
- return 0;
- }
- static int __f2fs_convert_inline_data(struct inode *inode, struct page *page)
- {
- int err;
- struct page *ipage;
- struct dnode_of_data dn;
- void *src_addr, *dst_addr;
- block_t new_blk_addr;
- struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
- struct f2fs_io_info fio = {
- .type = DATA,
- .rw = WRITE_SYNC | REQ_PRIO,
- };
- f2fs_lock_op(sbi);
- ipage = get_node_page(sbi, inode->i_ino);
- if (IS_ERR(ipage)) {
- err = PTR_ERR(ipage);
- goto out;
- }
- /*
- * i_addr[0] is not used for inline data,
- * so reserving new block will not destroy inline data
- */
- set_new_dnode(&dn, inode, ipage, NULL, 0);
- err = f2fs_reserve_block(&dn, 0);
- if (err)
- goto out;
- f2fs_wait_on_page_writeback(page, DATA);
- zero_user_segment(page, MAX_INLINE_DATA, PAGE_CACHE_SIZE);
- /* Copy the whole inline data block */
- src_addr = inline_data_addr(ipage);
- dst_addr = kmap(page);
- memcpy(dst_addr, src_addr, MAX_INLINE_DATA);
- kunmap(page);
- SetPageUptodate(page);
- /* write data page to try to make data consistent */
- set_page_writeback(page);
- write_data_page(page, &dn, &new_blk_addr, &fio);
- update_extent_cache(new_blk_addr, &dn);
- f2fs_wait_on_page_writeback(page, DATA);
- /* clear inline data and flag after data writeback */
- zero_user_segment(ipage, INLINE_DATA_OFFSET,
- INLINE_DATA_OFFSET + MAX_INLINE_DATA);
- clear_inode_flag(F2FS_I(inode), FI_INLINE_DATA);
- stat_dec_inline_inode(inode);
- sync_inode_page(&dn);
- f2fs_put_dnode(&dn);
- out:
- f2fs_unlock_op(sbi);
- return err;
- }
- int f2fs_convert_inline_data(struct inode *inode, pgoff_t to_size)
- {
- struct page *page;
- int err;
- if (!f2fs_has_inline_data(inode))
- return 0;
- else if (to_size <= MAX_INLINE_DATA)
- return 0;
- page = grab_cache_page(inode->i_mapping, 0);
- if (!page)
- return -ENOMEM;
- err = __f2fs_convert_inline_data(inode, page);
- f2fs_put_page(page, 1);
- return err;
- }
- int f2fs_write_inline_data(struct inode *inode,
- struct page *page, unsigned size)
- {
- void *src_addr, *dst_addr;
- struct page *ipage;
- struct dnode_of_data dn;
- int err;
- set_new_dnode(&dn, inode, NULL, NULL, 0);
- err = get_dnode_of_data(&dn, 0, LOOKUP_NODE);
- if (err)
- return err;
- ipage = dn.inode_page;
- f2fs_wait_on_page_writeback(ipage, NODE);
- zero_user_segment(ipage, INLINE_DATA_OFFSET,
- INLINE_DATA_OFFSET + MAX_INLINE_DATA);
- src_addr = kmap(page);
- dst_addr = inline_data_addr(ipage);
- memcpy(dst_addr, src_addr, size);
- kunmap(page);
- /* Release the first data block if it is allocated */
- if (!f2fs_has_inline_data(inode)) {
- truncate_data_blocks_range(&dn, 1);
- set_inode_flag(F2FS_I(inode), FI_INLINE_DATA);
- stat_inc_inline_inode(inode);
- }
- sync_inode_page(&dn);
- f2fs_put_dnode(&dn);
- return 0;
- }
- void truncate_inline_data(struct inode *inode, u64 from)
- {
- struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
- struct page *ipage;
- if (from >= MAX_INLINE_DATA)
- return;
- ipage = get_node_page(sbi, inode->i_ino);
- if (IS_ERR(ipage))
- return;
- f2fs_wait_on_page_writeback(ipage, NODE);
- zero_user_segment(ipage, INLINE_DATA_OFFSET + from,
- INLINE_DATA_OFFSET + MAX_INLINE_DATA);
- set_page_dirty(ipage);
- f2fs_put_page(ipage, 1);
- }
- int recover_inline_data(struct inode *inode, struct page *npage)
- {
- struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
- struct f2fs_inode *ri = NULL;
- void *src_addr, *dst_addr;
- struct page *ipage;
- /*
- * The inline_data recovery policy is as follows.
- * [prev.] [next] of inline_data flag
- * o o -> recover inline_data
- * o x -> remove inline_data, and then recover data blocks
- * x o -> remove inline_data, and then recover inline_data
- * x x -> recover data blocks
- */
- if (IS_INODE(npage))
- ri = F2FS_INODE(npage);
- if (f2fs_has_inline_data(inode) &&
- ri && ri->i_inline & F2FS_INLINE_DATA) {
- process_inline:
- ipage = get_node_page(sbi, inode->i_ino);
- f2fs_bug_on(IS_ERR(ipage));
- f2fs_wait_on_page_writeback(ipage, NODE);
- src_addr = inline_data_addr(npage);
- dst_addr = inline_data_addr(ipage);
- memcpy(dst_addr, src_addr, MAX_INLINE_DATA);
- update_inode(inode, ipage);
- f2fs_put_page(ipage, 1);
- return -1;
- }
- if (f2fs_has_inline_data(inode)) {
- ipage = get_node_page(sbi, inode->i_ino);
- f2fs_bug_on(IS_ERR(ipage));
- f2fs_wait_on_page_writeback(ipage, NODE);
- zero_user_segment(ipage, INLINE_DATA_OFFSET,
- INLINE_DATA_OFFSET + MAX_INLINE_DATA);
- clear_inode_flag(F2FS_I(inode), FI_INLINE_DATA);
- update_inode(inode, ipage);
- f2fs_put_page(ipage, 1);
- } else if (ri && ri->i_inline & F2FS_INLINE_DATA) {
- truncate_blocks(inode, 0);
- set_inode_flag(F2FS_I(inode), FI_INLINE_DATA);
- goto process_inline;
- }
- return 0;
- }
|