|
@@ -25,6 +25,7 @@
|
|
#define __reverse_ffz(x) __reverse_ffs(~(x))
|
|
#define __reverse_ffz(x) __reverse_ffs(~(x))
|
|
|
|
|
|
static struct kmem_cache *discard_entry_slab;
|
|
static struct kmem_cache *discard_entry_slab;
|
|
|
|
+static struct kmem_cache *sit_entry_set_slab;
|
|
|
|
|
|
/*
|
|
/*
|
|
* __reverse_ffs is copied from include/asm-generic/bitops/__ffs.h since
|
|
* __reverse_ffs is copied from include/asm-generic/bitops/__ffs.h since
|
|
@@ -492,11 +493,16 @@ void clear_prefree_segments(struct f2fs_sb_info *sbi)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-static void __mark_sit_entry_dirty(struct f2fs_sb_info *sbi, unsigned int segno)
|
|
|
|
|
|
+static bool __mark_sit_entry_dirty(struct f2fs_sb_info *sbi, unsigned int segno)
|
|
{
|
|
{
|
|
struct sit_info *sit_i = SIT_I(sbi);
|
|
struct sit_info *sit_i = SIT_I(sbi);
|
|
- if (!__test_and_set_bit(segno, sit_i->dirty_sentries_bitmap))
|
|
|
|
|
|
+
|
|
|
|
+ if (!__test_and_set_bit(segno, sit_i->dirty_sentries_bitmap)) {
|
|
sit_i->dirty_sentries++;
|
|
sit_i->dirty_sentries++;
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return true;
|
|
}
|
|
}
|
|
|
|
|
|
static void __set_sit_entry_type(struct f2fs_sb_info *sbi, int type,
|
|
static void __set_sit_entry_type(struct f2fs_sb_info *sbi, int type,
|
|
@@ -1443,27 +1449,86 @@ static struct page *get_next_sit_page(struct f2fs_sb_info *sbi,
|
|
return dst_page;
|
|
return dst_page;
|
|
}
|
|
}
|
|
|
|
|
|
-static bool flush_sits_in_journal(struct f2fs_sb_info *sbi)
|
|
|
|
|
|
+static struct sit_entry_set *grab_sit_entry_set(void)
|
|
|
|
+{
|
|
|
|
+ struct sit_entry_set *ses =
|
|
|
|
+ f2fs_kmem_cache_alloc(sit_entry_set_slab, GFP_ATOMIC);
|
|
|
|
+
|
|
|
|
+ ses->entry_cnt = 0;
|
|
|
|
+ INIT_LIST_HEAD(&ses->set_list);
|
|
|
|
+ return ses;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void release_sit_entry_set(struct sit_entry_set *ses)
|
|
|
|
+{
|
|
|
|
+ list_del(&ses->set_list);
|
|
|
|
+ kmem_cache_free(sit_entry_set_slab, ses);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void adjust_sit_entry_set(struct sit_entry_set *ses,
|
|
|
|
+ struct list_head *head)
|
|
|
|
+{
|
|
|
|
+ struct sit_entry_set *next = ses;
|
|
|
|
+
|
|
|
|
+ if (list_is_last(&ses->set_list, head))
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ list_for_each_entry_continue(next, head, set_list)
|
|
|
|
+ if (ses->entry_cnt <= next->entry_cnt)
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ list_move_tail(&ses->set_list, &next->set_list);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void add_sit_entry(unsigned int segno, struct list_head *head)
|
|
|
|
+{
|
|
|
|
+ struct sit_entry_set *ses;
|
|
|
|
+ unsigned int start_segno = START_SEGNO(segno);
|
|
|
|
+
|
|
|
|
+ list_for_each_entry(ses, head, set_list) {
|
|
|
|
+ if (ses->start_segno == start_segno) {
|
|
|
|
+ ses->entry_cnt++;
|
|
|
|
+ adjust_sit_entry_set(ses, head);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ ses = grab_sit_entry_set();
|
|
|
|
+
|
|
|
|
+ ses->start_segno = start_segno;
|
|
|
|
+ ses->entry_cnt++;
|
|
|
|
+ list_add(&ses->set_list, head);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void add_sits_in_set(struct f2fs_sb_info *sbi)
|
|
|
|
+{
|
|
|
|
+ struct f2fs_sm_info *sm_info = SM_I(sbi);
|
|
|
|
+ struct list_head *set_list = &sm_info->sit_entry_set;
|
|
|
|
+ unsigned long *bitmap = SIT_I(sbi)->dirty_sentries_bitmap;
|
|
|
|
+ unsigned long nsegs = TOTAL_SEGS(sbi);
|
|
|
|
+ unsigned int segno;
|
|
|
|
+
|
|
|
|
+ for_each_set_bit(segno, bitmap, nsegs)
|
|
|
|
+ add_sit_entry(segno, set_list);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void remove_sits_in_journal(struct f2fs_sb_info *sbi)
|
|
{
|
|
{
|
|
struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_COLD_DATA);
|
|
struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_COLD_DATA);
|
|
struct f2fs_summary_block *sum = curseg->sum_blk;
|
|
struct f2fs_summary_block *sum = curseg->sum_blk;
|
|
int i;
|
|
int i;
|
|
|
|
|
|
- /*
|
|
|
|
- * If the journal area in the current summary is full of sit entries,
|
|
|
|
- * all the sit entries will be flushed. Otherwise the sit entries
|
|
|
|
- * are not able to replace with newly hot sit entries.
|
|
|
|
- */
|
|
|
|
- if (sits_in_cursum(sum) >= SIT_JOURNAL_ENTRIES) {
|
|
|
|
- for (i = sits_in_cursum(sum) - 1; i >= 0; i--) {
|
|
|
|
- unsigned int segno;
|
|
|
|
- segno = le32_to_cpu(segno_in_journal(sum, i));
|
|
|
|
- __mark_sit_entry_dirty(sbi, segno);
|
|
|
|
- }
|
|
|
|
- update_sits_in_cursum(sum, -sits_in_cursum(sum));
|
|
|
|
- return true;
|
|
|
|
|
|
+ for (i = sits_in_cursum(sum) - 1; i >= 0; i--) {
|
|
|
|
+ unsigned int segno;
|
|
|
|
+ bool dirtied;
|
|
|
|
+
|
|
|
|
+ segno = le32_to_cpu(segno_in_journal(sum, i));
|
|
|
|
+ dirtied = __mark_sit_entry_dirty(sbi, segno);
|
|
|
|
+
|
|
|
|
+ if (!dirtied)
|
|
|
|
+ add_sit_entry(segno, &SM_I(sbi)->sit_entry_set);
|
|
}
|
|
}
|
|
- return false;
|
|
|
|
|
|
+ update_sits_in_cursum(sum, -sits_in_cursum(sum));
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
/*
|
|
@@ -1476,68 +1541,95 @@ void flush_sit_entries(struct f2fs_sb_info *sbi)
|
|
unsigned long *bitmap = sit_i->dirty_sentries_bitmap;
|
|
unsigned long *bitmap = sit_i->dirty_sentries_bitmap;
|
|
struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_COLD_DATA);
|
|
struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_COLD_DATA);
|
|
struct f2fs_summary_block *sum = curseg->sum_blk;
|
|
struct f2fs_summary_block *sum = curseg->sum_blk;
|
|
|
|
+ struct sit_entry_set *ses, *tmp;
|
|
|
|
+ struct list_head *head = &SM_I(sbi)->sit_entry_set;
|
|
unsigned long nsegs = TOTAL_SEGS(sbi);
|
|
unsigned long nsegs = TOTAL_SEGS(sbi);
|
|
- struct page *page = NULL;
|
|
|
|
- struct f2fs_sit_block *raw_sit = NULL;
|
|
|
|
- unsigned int start = 0, end = 0;
|
|
|
|
- unsigned int segno;
|
|
|
|
- bool flushed;
|
|
|
|
|
|
+ bool to_journal = true;
|
|
|
|
|
|
mutex_lock(&curseg->curseg_mutex);
|
|
mutex_lock(&curseg->curseg_mutex);
|
|
mutex_lock(&sit_i->sentry_lock);
|
|
mutex_lock(&sit_i->sentry_lock);
|
|
|
|
|
|
/*
|
|
/*
|
|
- * "flushed" indicates whether sit entries in journal are flushed
|
|
|
|
- * to the SIT area or not.
|
|
|
|
|
|
+ * add and account sit entries of dirty bitmap in sit entry
|
|
|
|
+ * set temporarily
|
|
*/
|
|
*/
|
|
- flushed = flush_sits_in_journal(sbi);
|
|
|
|
-
|
|
|
|
- for_each_set_bit(segno, bitmap, nsegs) {
|
|
|
|
- struct seg_entry *se = get_seg_entry(sbi, segno);
|
|
|
|
- int sit_offset, offset;
|
|
|
|
|
|
+ add_sits_in_set(sbi);
|
|
|
|
|
|
- sit_offset = SIT_ENTRY_OFFSET(sit_i, segno);
|
|
|
|
-
|
|
|
|
- /* add discard candidates */
|
|
|
|
- if (SM_I(sbi)->nr_discards < SM_I(sbi)->max_discards)
|
|
|
|
- add_discard_addrs(sbi, segno, se);
|
|
|
|
|
|
+ /*
|
|
|
|
+ * if there are no enough space in journal to store dirty sit
|
|
|
|
+ * entries, remove all entries from journal and add and account
|
|
|
|
+ * them in sit entry set.
|
|
|
|
+ */
|
|
|
|
+ if (!__has_cursum_space(sum, sit_i->dirty_sentries, SIT_JOURNAL))
|
|
|
|
+ remove_sits_in_journal(sbi);
|
|
|
|
|
|
- if (flushed)
|
|
|
|
- goto to_sit_page;
|
|
|
|
|
|
+ if (!sit_i->dirty_sentries)
|
|
|
|
+ goto out;
|
|
|
|
|
|
- offset = lookup_journal_in_cursum(sum, SIT_JOURNAL, segno, 1);
|
|
|
|
- if (offset >= 0) {
|
|
|
|
- segno_in_journal(sum, offset) = cpu_to_le32(segno);
|
|
|
|
- seg_info_to_raw_sit(se, &sit_in_journal(sum, offset));
|
|
|
|
- goto flush_done;
|
|
|
|
|
|
+ /*
|
|
|
|
+ * there are two steps to flush sit entries:
|
|
|
|
+ * #1, flush sit entries to journal in current cold data summary block.
|
|
|
|
+ * #2, flush sit entries to sit page.
|
|
|
|
+ */
|
|
|
|
+ list_for_each_entry_safe(ses, tmp, head, set_list) {
|
|
|
|
+ struct page *page;
|
|
|
|
+ struct f2fs_sit_block *raw_sit = NULL;
|
|
|
|
+ unsigned int start_segno = ses->start_segno;
|
|
|
|
+ unsigned int end = min(start_segno + SIT_ENTRY_PER_BLOCK,
|
|
|
|
+ nsegs);
|
|
|
|
+ unsigned int segno = start_segno;
|
|
|
|
+
|
|
|
|
+ if (to_journal &&
|
|
|
|
+ !__has_cursum_space(sum, ses->entry_cnt, SIT_JOURNAL))
|
|
|
|
+ to_journal = false;
|
|
|
|
+
|
|
|
|
+ if (!to_journal) {
|
|
|
|
+ page = get_next_sit_page(sbi, start_segno);
|
|
|
|
+ raw_sit = page_address(page);
|
|
}
|
|
}
|
|
-to_sit_page:
|
|
|
|
- if (!page || (start > segno) || (segno > end)) {
|
|
|
|
- if (page) {
|
|
|
|
- f2fs_put_page(page, 1);
|
|
|
|
- page = NULL;
|
|
|
|
- }
|
|
|
|
|
|
|
|
- start = START_SEGNO(segno);
|
|
|
|
- end = start + SIT_ENTRY_PER_BLOCK - 1;
|
|
|
|
|
|
+ /* flush dirty sit entries in region of current sit set */
|
|
|
|
+ for_each_set_bit_from(segno, bitmap, end) {
|
|
|
|
+ int offset, sit_offset;
|
|
|
|
+ struct seg_entry *se = get_seg_entry(sbi, segno);
|
|
|
|
+
|
|
|
|
+ /* add discard candidates */
|
|
|
|
+ if (SM_I(sbi)->nr_discards < SM_I(sbi)->max_discards)
|
|
|
|
+ add_discard_addrs(sbi, segno, se);
|
|
|
|
+
|
|
|
|
+ if (to_journal) {
|
|
|
|
+ offset = lookup_journal_in_cursum(sum,
|
|
|
|
+ SIT_JOURNAL, segno, 1);
|
|
|
|
+ f2fs_bug_on(sbi, offset < 0);
|
|
|
|
+ segno_in_journal(sum, offset) =
|
|
|
|
+ cpu_to_le32(segno);
|
|
|
|
+ seg_info_to_raw_sit(se,
|
|
|
|
+ &sit_in_journal(sum, offset));
|
|
|
|
+ } else {
|
|
|
|
+ sit_offset = SIT_ENTRY_OFFSET(sit_i, segno);
|
|
|
|
+ seg_info_to_raw_sit(se,
|
|
|
|
+ &raw_sit->entries[sit_offset]);
|
|
|
|
+ }
|
|
|
|
|
|
- /* read sit block that will be updated */
|
|
|
|
- page = get_next_sit_page(sbi, start);
|
|
|
|
- raw_sit = page_address(page);
|
|
|
|
|
|
+ __clear_bit(segno, bitmap);
|
|
|
|
+ sit_i->dirty_sentries--;
|
|
|
|
+ ses->entry_cnt--;
|
|
}
|
|
}
|
|
|
|
|
|
- /* udpate entry in SIT block */
|
|
|
|
- seg_info_to_raw_sit(se, &raw_sit->entries[sit_offset]);
|
|
|
|
-flush_done:
|
|
|
|
- __clear_bit(segno, bitmap);
|
|
|
|
- sit_i->dirty_sentries--;
|
|
|
|
|
|
+ if (!to_journal)
|
|
|
|
+ f2fs_put_page(page, 1);
|
|
|
|
+
|
|
|
|
+ f2fs_bug_on(sbi, ses->entry_cnt);
|
|
|
|
+ release_sit_entry_set(ses);
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ f2fs_bug_on(sbi, !list_empty(head));
|
|
|
|
+ f2fs_bug_on(sbi, sit_i->dirty_sentries);
|
|
|
|
+
|
|
|
|
+out:
|
|
mutex_unlock(&sit_i->sentry_lock);
|
|
mutex_unlock(&sit_i->sentry_lock);
|
|
mutex_unlock(&curseg->curseg_mutex);
|
|
mutex_unlock(&curseg->curseg_mutex);
|
|
|
|
|
|
- /* writeout last modified SIT block */
|
|
|
|
- f2fs_put_page(page, 1);
|
|
|
|
-
|
|
|
|
set_prefree_as_free_segments(sbi);
|
|
set_prefree_as_free_segments(sbi);
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1854,6 +1946,8 @@ int build_segment_manager(struct f2fs_sb_info *sbi)
|
|
sm_info->nr_discards = 0;
|
|
sm_info->nr_discards = 0;
|
|
sm_info->max_discards = 0;
|
|
sm_info->max_discards = 0;
|
|
|
|
|
|
|
|
+ INIT_LIST_HEAD(&sm_info->sit_entry_set);
|
|
|
|
+
|
|
if (test_opt(sbi, FLUSH_MERGE) && !f2fs_readonly(sbi->sb)) {
|
|
if (test_opt(sbi, FLUSH_MERGE) && !f2fs_readonly(sbi->sb)) {
|
|
err = create_flush_cmd_control(sbi);
|
|
err = create_flush_cmd_control(sbi);
|
|
if (err)
|
|
if (err)
|
|
@@ -1983,11 +2077,22 @@ int __init create_segment_manager_caches(void)
|
|
discard_entry_slab = f2fs_kmem_cache_create("discard_entry",
|
|
discard_entry_slab = f2fs_kmem_cache_create("discard_entry",
|
|
sizeof(struct discard_entry));
|
|
sizeof(struct discard_entry));
|
|
if (!discard_entry_slab)
|
|
if (!discard_entry_slab)
|
|
- return -ENOMEM;
|
|
|
|
|
|
+ goto fail;
|
|
|
|
+
|
|
|
|
+ sit_entry_set_slab = f2fs_kmem_cache_create("sit_entry_set",
|
|
|
|
+ sizeof(struct nat_entry_set));
|
|
|
|
+ if (!sit_entry_set_slab)
|
|
|
|
+ goto destory_discard_entry;
|
|
return 0;
|
|
return 0;
|
|
|
|
+
|
|
|
|
+destory_discard_entry:
|
|
|
|
+ kmem_cache_destroy(discard_entry_slab);
|
|
|
|
+fail:
|
|
|
|
+ return -ENOMEM;
|
|
}
|
|
}
|
|
|
|
|
|
void destroy_segment_manager_caches(void)
|
|
void destroy_segment_manager_caches(void)
|
|
{
|
|
{
|
|
|
|
+ kmem_cache_destroy(sit_entry_set_slab);
|
|
kmem_cache_destroy(discard_entry_slab);
|
|
kmem_cache_destroy(discard_entry_slab);
|
|
}
|
|
}
|