2013-11-10 19:13:19 +04:00
/*
* 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 )
{
block_t nr_blocks ;
loff_t i_size ;
2014-09-03 02:31:18 +04:00
if ( ! test_opt ( F2FS_I_SB ( inode ) , INLINE_DATA ) )
2013-11-10 19:13:19 +04:00
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 page * ipage ;
void * src_addr , * dst_addr ;
2013-12-30 14:36:23 +04:00
if ( page - > index ) {
zero_user_segment ( page , 0 , PAGE_CACHE_SIZE ) ;
goto out ;
}
2014-09-03 02:31:18 +04:00
ipage = get_node_page ( F2FS_I_SB ( inode ) , inode - > i_ino ) ;
2014-03-29 11:30:40 +04:00
if ( IS_ERR ( ipage ) ) {
unlock_page ( page ) ;
2013-11-10 19:13:19 +04:00
return PTR_ERR ( ipage ) ;
2014-03-29 11:30:40 +04:00
}
2013-11-10 19:13:19 +04:00
2013-12-30 05:29:06 +04:00
zero_user_segment ( page , MAX_INLINE_DATA , PAGE_CACHE_SIZE ) ;
2013-11-10 19:13:19 +04:00
/* 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 ) ;
2013-12-30 14:36:23 +04:00
out :
2013-11-10 19:13:19 +04:00
SetPageUptodate ( page ) ;
unlock_page ( page ) ;
return 0 ;
}
static int __f2fs_convert_inline_data ( struct inode * inode , struct page * page )
{
2014-08-19 01:41:11 +04:00
int err = 0 ;
2013-11-10 19:13:19 +04:00
struct page * ipage ;
struct dnode_of_data dn ;
void * src_addr , * dst_addr ;
block_t new_blk_addr ;
2014-09-03 02:31:18 +04:00
struct f2fs_sb_info * sbi = F2FS_I_SB ( inode ) ;
2013-11-10 19:13:19 +04:00
struct f2fs_io_info fio = {
. type = DATA ,
. rw = WRITE_SYNC | REQ_PRIO ,
} ;
f2fs_lock_op ( sbi ) ;
ipage = get_node_page ( sbi , inode - > i_ino ) ;
2014-04-16 09:22:50 +04:00
if ( IS_ERR ( ipage ) ) {
err = PTR_ERR ( ipage ) ;
goto out ;
}
2013-11-10 19:13:19 +04:00
2014-08-19 01:41:11 +04:00
/* someone else converted inline_data already */
if ( ! f2fs_has_inline_data ( inode ) )
goto out ;
2013-11-10 19:13:19 +04:00
/*
* i_addr [ 0 ] is not used for inline data ,
* so reserving new block will not destroy inline data
*/
2013-12-27 12:04:17 +04:00
set_new_dnode ( & dn , inode , ipage , NULL , 0 ) ;
2013-11-10 19:13:19 +04:00
err = f2fs_reserve_block ( & dn , 0 ) ;
2014-04-16 09:22:50 +04:00
if ( err )
goto out ;
2013-11-10 19:13:19 +04:00
2014-04-29 12:35:10 +04:00
f2fs_wait_on_page_writeback ( page , DATA ) ;
2013-12-30 05:29:06 +04:00
zero_user_segment ( page , MAX_INLINE_DATA , PAGE_CACHE_SIZE ) ;
2013-11-10 19:13:19 +04:00
/* 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 ) ;
2013-12-27 07:28:59 +04:00
SetPageUptodate ( page ) ;
2013-11-10 19:13:19 +04:00
/* 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 ) ;
2014-01-10 11:26:14 +04:00
f2fs_wait_on_page_writeback ( page , DATA ) ;
2013-11-10 19:13:19 +04:00
/* 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 ) ;
2013-11-26 06:08:57 +04:00
stat_dec_inline_inode ( inode ) ;
2013-11-10 19:13:19 +04:00
sync_inode_page ( & dn ) ;
2013-12-27 12:04:17 +04:00
f2fs_put_dnode ( & dn ) ;
2014-04-16 09:22:50 +04:00
out :
2013-11-10 19:13:19 +04:00
f2fs_unlock_op ( sbi ) ;
return err ;
}
2014-08-08 03:32:25 +04:00
int f2fs_convert_inline_data ( struct inode * inode , pgoff_t to_size ,
struct page * page )
2013-11-10 19:13:19 +04:00
{
2014-08-08 03:32:25 +04:00
struct page * new_page = page ;
2013-12-27 07:28:59 +04:00
int err ;
2013-11-10 19:13:19 +04:00
2013-12-27 07:28:59 +04:00
if ( ! f2fs_has_inline_data ( inode ) )
return 0 ;
else if ( to_size < = MAX_INLINE_DATA )
return 0 ;
2013-11-10 19:13:19 +04:00
2014-08-08 03:32:25 +04:00
if ( ! page | | page - > index ! = 0 ) {
new_page = grab_cache_page ( inode - > i_mapping , 0 ) ;
if ( ! new_page )
return - ENOMEM ;
}
2013-11-10 19:13:19 +04:00
2014-08-08 03:32:25 +04:00
err = __f2fs_convert_inline_data ( inode , new_page ) ;
if ( ! page | | page - > index ! = 0 )
f2fs_put_page ( new_page , 1 ) ;
2013-11-10 19:13:19 +04:00
return err ;
}
int f2fs_write_inline_data ( struct inode * inode ,
2014-08-08 03:32:25 +04:00
struct page * page , unsigned size )
2013-11-10 19:13:19 +04:00
{
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 ;
2014-04-29 12:28:32 +04:00
f2fs_wait_on_page_writeback ( ipage , NODE ) ;
2013-11-10 19:13:19 +04:00
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 ) ;
2013-11-26 06:08:57 +04:00
stat_inc_inline_inode ( inode ) ;
2013-11-10 19:13:19 +04:00
}
2014-07-25 18:40:59 +04:00
set_inode_flag ( F2FS_I ( inode ) , FI_APPEND_WRITE ) ;
2013-11-10 19:13:19 +04:00
sync_inode_page ( & dn ) ;
f2fs_put_dnode ( & dn ) ;
return 0 ;
}
2013-12-26 07:49:48 +04:00
2014-04-29 05:03:03 +04:00
void truncate_inline_data ( struct inode * inode , u64 from )
{
struct page * ipage ;
if ( from > = MAX_INLINE_DATA )
return ;
2014-09-03 02:31:18 +04:00
ipage = get_node_page ( F2FS_I_SB ( inode ) , inode - > i_ino ) ;
2014-04-29 05:03:03 +04:00
if ( IS_ERR ( ipage ) )
return ;
2014-04-29 12:28:32 +04:00
f2fs_wait_on_page_writeback ( ipage , NODE ) ;
2014-04-29 05:03:03 +04:00
zero_user_segment ( ipage , INLINE_DATA_OFFSET + from ,
INLINE_DATA_OFFSET + MAX_INLINE_DATA ) ;
set_page_dirty ( ipage ) ;
f2fs_put_page ( ipage , 1 ) ;
}
2014-08-08 03:57:17 +04:00
bool recover_inline_data ( struct inode * inode , struct page * npage )
2013-12-26 07:49:48 +04:00
{
2014-09-03 02:31:18 +04:00
struct f2fs_sb_info * sbi = F2FS_I_SB ( inode ) ;
2013-12-26 07:49:48 +04:00
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 ) & &
2014-08-08 03:57:17 +04:00
ri & & ( ri - > i_inline & F2FS_INLINE_DATA ) ) {
2013-12-26 07:49:48 +04:00
process_inline :
ipage = get_node_page ( sbi , inode - > i_ino ) ;
f2fs_bug_on ( IS_ERR ( ipage ) ) ;
2014-04-29 12:28:32 +04:00
f2fs_wait_on_page_writeback ( ipage , NODE ) ;
2013-12-26 07:49:48 +04:00
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 ) ;
2014-08-08 03:57:17 +04:00
return true ;
2013-12-26 07:49:48 +04:00
}
if ( f2fs_has_inline_data ( inode ) ) {
ipage = get_node_page ( sbi , inode - > i_ino ) ;
f2fs_bug_on ( IS_ERR ( ipage ) ) ;
2014-04-29 12:28:32 +04:00
f2fs_wait_on_page_writeback ( ipage , NODE ) ;
2013-12-26 07:49:48 +04:00
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 ) ;
2014-08-08 03:57:17 +04:00
} else if ( ri & & ( ri - > i_inline & F2FS_INLINE_DATA ) ) {
2014-08-15 03:32:54 +04:00
truncate_blocks ( inode , 0 , false ) ;
2013-12-26 07:49:48 +04:00
set_inode_flag ( F2FS_I ( inode ) , FI_INLINE_DATA ) ;
goto process_inline ;
}
2014-08-08 03:57:17 +04:00
return false ;
2013-12-26 07:49:48 +04:00
}