@ -13,6 +13,7 @@
# include <linux/bio.h>
# include <linux/blkdev.h>
# include <linux/prefetch.h>
# include <linux/kthread.h>
# include <linux/vmalloc.h>
# include <linux/swap.h>
@ -24,6 +25,7 @@
# define __reverse_ffz(x) __reverse_ffs(~(x))
static struct kmem_cache * discard_entry_slab ;
static struct kmem_cache * flush_cmd_slab ;
/*
* __reverse_ffs is copied from include / asm - generic / bitops / __ffs . h since
@ -195,6 +197,73 @@ void f2fs_balance_fs_bg(struct f2fs_sb_info *sbi)
f2fs_sync_fs ( sbi - > sb , true ) ;
}
static int issue_flush_thread ( void * data )
{
struct f2fs_sb_info * sbi = data ;
struct f2fs_sm_info * sm_i = SM_I ( sbi ) ;
wait_queue_head_t * q = & sm_i - > flush_wait_queue ;
repeat :
if ( kthread_should_stop ( ) )
return 0 ;
spin_lock ( & sm_i - > issue_lock ) ;
if ( sm_i - > issue_list ) {
sm_i - > dispatch_list = sm_i - > issue_list ;
sm_i - > issue_list = sm_i - > issue_tail = NULL ;
}
spin_unlock ( & sm_i - > issue_lock ) ;
if ( sm_i - > dispatch_list ) {
struct bio * bio = bio_alloc ( GFP_NOIO , 0 ) ;
struct flush_cmd * cmd , * next ;
int ret ;
bio - > bi_bdev = sbi - > sb - > s_bdev ;
ret = submit_bio_wait ( WRITE_FLUSH , bio ) ;
for ( cmd = sm_i - > dispatch_list ; cmd ; cmd = next ) {
cmd - > ret = ret ;
next = cmd - > next ;
complete ( & cmd - > wait ) ;
}
sm_i - > dispatch_list = NULL ;
}
wait_event_interruptible ( * q , kthread_should_stop ( ) | | sm_i - > issue_list ) ;
goto repeat ;
}
int f2fs_issue_flush ( struct f2fs_sb_info * sbi )
{
struct f2fs_sm_info * sm_i = SM_I ( sbi ) ;
struct flush_cmd * cmd ;
int ret ;
if ( ! test_opt ( sbi , FLUSH_MERGE ) )
return blkdev_issue_flush ( sbi - > sb - > s_bdev , GFP_KERNEL , NULL ) ;
cmd = f2fs_kmem_cache_alloc ( flush_cmd_slab , GFP_ATOMIC ) ;
cmd - > next = NULL ;
cmd - > ret = 0 ;
init_completion ( & cmd - > wait ) ;
spin_lock ( & sm_i - > issue_lock ) ;
if ( sm_i - > issue_list )
sm_i - > issue_tail - > next = cmd ;
else
sm_i - > issue_list = cmd ;
sm_i - > issue_tail = cmd ;
spin_unlock ( & sm_i - > issue_lock ) ;
if ( ! sm_i - > dispatch_list )
wake_up ( & sm_i - > flush_wait_queue ) ;
wait_for_completion ( & cmd - > wait ) ;
ret = cmd - > ret ;
kmem_cache_free ( flush_cmd_slab , cmd ) ;
return ret ;
}
static void __locate_dirty_segment ( struct f2fs_sb_info * sbi , unsigned int segno ,
enum dirty_type dirty_type )
{
@ -1763,6 +1832,7 @@ int build_segment_manager(struct f2fs_sb_info *sbi)
{
struct f2fs_super_block * raw_super = F2FS_RAW_SUPER ( sbi ) ;
struct f2fs_checkpoint * ckpt = F2FS_CKPT ( sbi ) ;
dev_t dev = sbi - > sb - > s_bdev - > bd_dev ;
struct f2fs_sm_info * sm_info ;
int err ;
@ -1790,6 +1860,16 @@ int build_segment_manager(struct f2fs_sb_info *sbi)
sm_info - > nr_discards = 0 ;
sm_info - > max_discards = 0 ;
if ( test_opt ( sbi , FLUSH_MERGE ) ) {
spin_lock_init ( & sm_info - > issue_lock ) ;
init_waitqueue_head ( & sm_info - > flush_wait_queue ) ;
sm_info - > f2fs_issue_flush = kthread_run ( issue_flush_thread , sbi ,
" f2fs_flush-%u:%u " , MAJOR ( dev ) , MINOR ( dev ) ) ;
if ( IS_ERR ( sm_info - > f2fs_issue_flush ) )
return PTR_ERR ( sm_info - > f2fs_issue_flush ) ;
}
err = build_sit_info ( sbi ) ;
if ( err )
return err ;
@ -1898,6 +1978,8 @@ void destroy_segment_manager(struct f2fs_sb_info *sbi)
struct f2fs_sm_info * sm_info = SM_I ( sbi ) ;
if ( ! sm_info )
return ;
if ( sm_info - > f2fs_issue_flush )
kthread_stop ( sm_info - > f2fs_issue_flush ) ;
destroy_dirty_segmap ( sbi ) ;
destroy_curseg ( sbi ) ;
destroy_free_segmap ( sbi ) ;
@ -1912,10 +1994,17 @@ int __init create_segment_manager_caches(void)
sizeof ( struct discard_entry ) ) ;
if ( ! discard_entry_slab )
return - ENOMEM ;
flush_cmd_slab = f2fs_kmem_cache_create ( " flush_command " ,
sizeof ( struct flush_cmd ) ) ;
if ( ! flush_cmd_slab ) {
kmem_cache_destroy ( discard_entry_slab ) ;
return - ENOMEM ;
}
return 0 ;
}
void destroy_segment_manager_caches ( void )
{
kmem_cache_destroy ( discard_entry_slab ) ;
kmem_cache_destroy ( flush_cmd_slab ) ;
}