IB/srp: Fix kernel-doc warnings
[cascardo/linux.git] / fs / f2fs / segment.c
index 7caac5f..085f548 100644 (file)
@@ -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)
 {
@@ -340,8 +409,7 @@ static void set_prefree_as_free_segments(struct f2fs_sb_info *sbi)
 void clear_prefree_segments(struct f2fs_sb_info *sbi)
 {
        struct list_head *head = &(SM_I(sbi)->discard_list);
-       struct list_head *this, *next;
-       struct discard_entry *entry;
+       struct discard_entry *entry, *this;
        struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
        unsigned long *prefree_map = dirty_i->dirty_segmap[PRE];
        unsigned int total_segs = TOTAL_SEGS(sbi);
@@ -370,8 +438,7 @@ void clear_prefree_segments(struct f2fs_sb_info *sbi)
        mutex_unlock(&dirty_i->seglist_lock);
 
        /* send small discards */
-       list_for_each_safe(this, next, head) {
-               entry = list_entry(this, struct discard_entry, list);
+       list_for_each_entry_safe(entry, this, head, list) {
                f2fs_issue_discard(sbi, entry->blkaddr, entry->len);
                list_del(&entry->list);
                SM_I(sbi)->nr_discards -= entry->len;
@@ -405,7 +472,7 @@ static void update_sit_entry(struct f2fs_sb_info *sbi, block_t blkaddr, int del)
 
        se = get_seg_entry(sbi, segno);
        new_vblocks = se->valid_blocks + del;
-       offset = GET_SEGOFF_FROM_SEG0(sbi, blkaddr) & (sbi->blocks_per_seg - 1);
+       offset = GET_BLKOFF_FROM_SEG0(sbi, blkaddr);
 
        f2fs_bug_on((new_vblocks >> (sizeof(unsigned short) << 3) ||
                                (new_vblocks > sbi->blocks_per_seg)));
@@ -434,12 +501,14 @@ static void update_sit_entry(struct f2fs_sb_info *sbi, block_t blkaddr, int del)
                get_sec_entry(sbi, segno)->valid_blocks += del;
 }
 
-static void refresh_sit_entry(struct f2fs_sb_info *sbi,
-                       block_t old_blkaddr, block_t new_blkaddr)
+void refresh_sit_entry(struct f2fs_sb_info *sbi, block_t old, block_t new)
 {
-       update_sit_entry(sbi, new_blkaddr, 1);
-       if (GET_SEGNO(sbi, old_blkaddr) != NULL_SEGNO)
-               update_sit_entry(sbi, old_blkaddr, -1);
+       update_sit_entry(sbi, new, 1);
+       if (GET_SEGNO(sbi, old) != NULL_SEGNO)
+               update_sit_entry(sbi, old, -1);
+
+       locate_dirty_segment(sbi, GET_SEGNO(sbi, old));
+       locate_dirty_segment(sbi, GET_SEGNO(sbi, new));
 }
 
 void invalidate_blocks(struct f2fs_sb_info *sbi, block_t addr)
@@ -881,17 +950,15 @@ void allocate_data_block(struct f2fs_sb_info *sbi, struct page *page,
 
        stat_inc_block_count(sbi, curseg);
 
+       if (!__has_curseg_space(sbi, type))
+               sit_i->s_ops->allocate_segment(sbi, type, false);
        /*
         * SIT information should be updated before segment allocation,
         * since SSR needs latest valid block information.
         */
        refresh_sit_entry(sbi, old_blkaddr, *new_blkaddr);
-
-       if (!__has_curseg_space(sbi, type))
-               sit_i->s_ops->allocate_segment(sbi, type, false);
-
        locate_dirty_segment(sbi, old_cursegno);
-       locate_dirty_segment(sbi, GET_SEGNO(sbi, old_blkaddr));
+
        mutex_unlock(&sit_i->sentry_lock);
 
        if (page && IS_NODESEG(type))
@@ -987,14 +1054,11 @@ void recover_data_page(struct f2fs_sb_info *sbi,
                change_curseg(sbi, type, true);
        }
 
-       curseg->next_blkoff = GET_SEGOFF_FROM_SEG0(sbi, new_blkaddr) &
-                                       (sbi->blocks_per_seg - 1);
+       curseg->next_blkoff = GET_BLKOFF_FROM_SEG0(sbi, new_blkaddr);
        __add_sum_entry(sbi, type, sum);
 
        refresh_sit_entry(sbi, old_blkaddr, new_blkaddr);
-
        locate_dirty_segment(sbi, old_cursegno);
-       locate_dirty_segment(sbi, GET_SEGNO(sbi, old_blkaddr));
 
        mutex_unlock(&sit_i->sentry_lock);
        mutex_unlock(&curseg->curseg_mutex);
@@ -1028,8 +1092,7 @@ void rewrite_node_page(struct f2fs_sb_info *sbi,
                curseg->next_segno = segno;
                change_curseg(sbi, type, true);
        }
-       curseg->next_blkoff = GET_SEGOFF_FROM_SEG0(sbi, new_blkaddr) &
-                                       (sbi->blocks_per_seg - 1);
+       curseg->next_blkoff = GET_BLKOFF_FROM_SEG0(sbi, new_blkaddr);
        __add_sum_entry(sbi, type, sum);
 
        /* change the current log to the next block addr in advance */
@@ -1037,28 +1100,50 @@ void rewrite_node_page(struct f2fs_sb_info *sbi,
                curseg->next_segno = next_segno;
                change_curseg(sbi, type, true);
        }
-       curseg->next_blkoff = GET_SEGOFF_FROM_SEG0(sbi, next_blkaddr) &
-                                       (sbi->blocks_per_seg - 1);
+       curseg->next_blkoff = GET_BLKOFF_FROM_SEG0(sbi, next_blkaddr);
 
        /* rewrite node page */
        set_page_writeback(page);
        f2fs_submit_page_mbio(sbi, page, new_blkaddr, &fio);
        f2fs_submit_merged_bio(sbi, NODE, WRITE);
        refresh_sit_entry(sbi, old_blkaddr, new_blkaddr);
-
        locate_dirty_segment(sbi, old_cursegno);
-       locate_dirty_segment(sbi, GET_SEGNO(sbi, old_blkaddr));
 
        mutex_unlock(&sit_i->sentry_lock);
        mutex_unlock(&curseg->curseg_mutex);
 }
 
+static inline bool is_merged_page(struct f2fs_sb_info *sbi,
+                                       struct page *page, enum page_type type)
+{
+       enum page_type btype = PAGE_TYPE_OF_BIO(type);
+       struct f2fs_bio_info *io = &sbi->write_io[btype];
+       struct bio_vec *bvec;
+       int i;
+
+       down_read(&io->io_rwsem);
+       if (!io->bio)
+               goto out;
+
+       bio_for_each_segment_all(bvec, io->bio, i) {
+               if (page == bvec->bv_page) {
+                       up_read(&io->io_rwsem);
+                       return true;
+               }
+       }
+
+out:
+       up_read(&io->io_rwsem);
+       return false;
+}
+
 void f2fs_wait_on_page_writeback(struct page *page,
                                enum page_type type)
 {
        struct f2fs_sb_info *sbi = F2FS_SB(page->mapping->host->i_sb);
        if (PageWriteback(page)) {
-               f2fs_submit_merged_bio(sbi, type, WRITE);
+               if (is_merged_page(sbi, page, type))
+                       f2fs_submit_merged_bio(sbi, type, WRITE);
                wait_on_page_writeback(page);
        }
 }
@@ -1167,9 +1252,12 @@ static int read_normal_summaries(struct f2fs_sb_info *sbi, int type)
                                ns->ofs_in_node = 0;
                        }
                } else {
-                       if (restore_node_summary(sbi, segno, sum)) {
+                       int err;
+
+                       err = restore_node_summary(sbi, segno, sum);
+                       if (err) {
                                f2fs_put_page(new, 1);
-                               return -EINVAL;
+                               return err;
                        }
                }
        }
@@ -1190,6 +1278,7 @@ static int read_normal_summaries(struct f2fs_sb_info *sbi, int type)
 static int restore_curseg_summaries(struct f2fs_sb_info *sbi)
 {
        int type = CURSEG_HOT_DATA;
+       int err;
 
        if (is_set_ckpt_flags(F2FS_CKPT(sbi), CP_COMPACT_SUM_FLAG)) {
                /* restore for compacted data summary */
@@ -1198,9 +1287,12 @@ static int restore_curseg_summaries(struct f2fs_sb_info *sbi)
                type = CURSEG_HOT_NODE;
        }
 
-       for (; type <= CURSEG_COLD_NODE; type++)
-               if (read_normal_summaries(sbi, type))
-                       return -EINVAL;
+       for (; type <= CURSEG_COLD_NODE; type++) {
+               err = read_normal_summaries(sbi, type);
+               if (err)
+                       return err;
+       }
+
        return 0;
 }
 
@@ -1583,47 +1675,6 @@ static int build_curseg(struct f2fs_sb_info *sbi)
        return restore_curseg_summaries(sbi);
 }
 
-static int ra_sit_pages(struct f2fs_sb_info *sbi, int start, int nrpages)
-{
-       struct address_space *mapping = META_MAPPING(sbi);
-       struct page *page;
-       block_t blk_addr, prev_blk_addr = 0;
-       int sit_blk_cnt = SIT_BLK_CNT(sbi);
-       int blkno = start;
-       struct f2fs_io_info fio = {
-               .type = META,
-               .rw = READ_SYNC | REQ_META | REQ_PRIO
-       };
-
-       for (; blkno < start + nrpages && blkno < sit_blk_cnt; blkno++) {
-
-               blk_addr = current_sit_addr(sbi, blkno * SIT_ENTRY_PER_BLOCK);
-
-               if (blkno != start && prev_blk_addr + 1 != blk_addr)
-                       break;
-               prev_blk_addr = blk_addr;
-repeat:
-               page = grab_cache_page(mapping, blk_addr);
-               if (!page) {
-                       cond_resched();
-                       goto repeat;
-               }
-               if (PageUptodate(page)) {
-                       mark_page_accessed(page);
-                       f2fs_put_page(page, 1);
-                       continue;
-               }
-
-               f2fs_submit_page_mbio(sbi, page, blk_addr, &fio);
-
-               mark_page_accessed(page);
-               f2fs_put_page(page, 0);
-       }
-
-       f2fs_submit_merged_bio(sbi, META, READ);
-       return blkno - start;
-}
-
 static void build_sit_entries(struct f2fs_sb_info *sbi)
 {
        struct sit_info *sit_i = SIT_I(sbi);
@@ -1635,7 +1686,7 @@ static void build_sit_entries(struct f2fs_sb_info *sbi)
        int nrpages = MAX_BIO_BLOCKS(max_hw_blocks(sbi));
 
        do {
-               readed = ra_sit_pages(sbi, start_blk, nrpages);
+               readed = ra_meta_pages(sbi, start_blk, nrpages, META_SIT);
 
                start = start_blk * sit_i->sents_per_block;
                end = (start_blk + readed) * sit_i->sents_per_block;
@@ -1781,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;
 
@@ -1799,7 +1851,8 @@ int build_segment_manager(struct f2fs_sb_info *sbi)
        sm_info->ovp_segments = le32_to_cpu(ckpt->overprov_segment_count);
        sm_info->main_segments = le32_to_cpu(raw_super->segment_count_main);
        sm_info->ssa_blkaddr = le32_to_cpu(raw_super->ssa_blkaddr);
-       sm_info->rec_prefree_segments = DEF_RECLAIM_PREFREE_SEGMENTS;
+       sm_info->rec_prefree_segments = sm_info->main_segments *
+                                       DEF_RECLAIM_PREFREE_SEGMENTS / 100;
        sm_info->ipu_policy = F2FS_IPU_DISABLE;
        sm_info->min_ipu_util = DEF_MIN_IPU_UTIL;
 
@@ -1807,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;
@@ -1915,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);
@@ -1926,13 +1991,20 @@ void destroy_segment_manager(struct f2fs_sb_info *sbi)
 int __init create_segment_manager_caches(void)
 {
        discard_entry_slab = f2fs_kmem_cache_create("discard_entry",
-                       sizeof(struct discard_entry), NULL);
+                       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);
 }