aboutsummaryrefslogtreecommitdiffstats
path: root/fs/f2fs/segment.c
diff options
context:
space:
mode:
authorJaegeuk Kim <jaegeuk.kim@samsung.com>2013-11-12 00:49:56 -0500
committerJaegeuk Kim <jaegeuk.kim@samsung.com>2013-12-22 20:18:00 -0500
commitb29555505d81e496fdbd125190c8043f3c09a83c (patch)
treec0043de3cbcea1f447c3414c1a5e63e007f2da2e /fs/f2fs/segment.c
parent7fd9e544fbb10c6ae4b4953f6063560c8eeae6e8 (diff)
f2fs: add key functions for small discards
This patch adds key functions to activate the small discard feature. Note that this procedure is conducted during the checkpoint only. In flush_sit_entries(), when a new dirty sit entry is flushed, f2fs calls add_discard_addrs() which searches candidates to be discarded. The candidates should be marked *invalidated* and also previous checkpoint recognizes it as *valid*. At the end of a checkpoint procedure, f2fs throws discards based on the discard entry list. Signed-off-by: Jaegeuk Kim <jaegeuk.kim@samsung.com>
Diffstat (limited to 'fs/f2fs/segment.c')
-rw-r--r--fs/f2fs/segment.c60
1 files changed, 60 insertions, 0 deletions
diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c
index 823526ec5243..505a8894cfa1 100644
--- a/fs/f2fs/segment.c
+++ b/fs/f2fs/segment.c
@@ -266,6 +266,47 @@ static void locate_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno)
266 mutex_unlock(&dirty_i->seglist_lock); 266 mutex_unlock(&dirty_i->seglist_lock);
267} 267}
268 268
269static void add_discard_addrs(struct f2fs_sb_info *sbi,
270 unsigned int segno, struct seg_entry *se)
271{
272 struct list_head *head = &SM_I(sbi)->discard_list;
273 struct discard_entry *new;
274 int entries = SIT_VBLOCK_MAP_SIZE / sizeof(unsigned long);
275 int max_blocks = sbi->blocks_per_seg;
276 unsigned long *cur_map = (unsigned long *)se->cur_valid_map;
277 unsigned long *ckpt_map = (unsigned long *)se->ckpt_valid_map;
278 unsigned long dmap[entries];
279 unsigned int start = 0, end = -1;
280 int i;
281
282 if (!test_opt(sbi, DISCARD))
283 return;
284
285 /* zero block will be discarded through the prefree list */
286 if (!se->valid_blocks || se->valid_blocks == max_blocks)
287 return;
288
289 /* SIT_VBLOCK_MAP_SIZE should be multiple of sizeof(unsigned long) */
290 for (i = 0; i < entries; i++)
291 dmap[i] = (cur_map[i] ^ ckpt_map[i]) & ckpt_map[i];
292
293 while (SM_I(sbi)->nr_discards <= SM_I(sbi)->max_discards) {
294 start = __find_rev_next_bit(dmap, max_blocks, end + 1);
295 if (start >= max_blocks)
296 break;
297
298 end = __find_rev_next_zero_bit(dmap, max_blocks, start + 1);
299
300 new = f2fs_kmem_cache_alloc(discard_entry_slab, GFP_NOFS);
301 INIT_LIST_HEAD(&new->list);
302 new->blkaddr = START_BLOCK(sbi, segno) + start;
303 new->len = end - start;
304
305 list_add_tail(&new->list, head);
306 SM_I(sbi)->nr_discards += end - start;
307 }
308}
309
269/* 310/*
270 * Should call clear_prefree_segments after checkpoint is done. 311 * Should call clear_prefree_segments after checkpoint is done.
271 */ 312 */
@@ -288,6 +329,9 @@ static void set_prefree_as_free_segments(struct f2fs_sb_info *sbi)
288 329
289void clear_prefree_segments(struct f2fs_sb_info *sbi) 330void clear_prefree_segments(struct f2fs_sb_info *sbi)
290{ 331{
332 struct list_head *head = &(SM_I(sbi)->discard_list);
333 struct list_head *this, *next;
334 struct discard_entry *entry;
291 struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); 335 struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
292 unsigned long *prefree_map = dirty_i->dirty_segmap[PRE]; 336 unsigned long *prefree_map = dirty_i->dirty_segmap[PRE];
293 unsigned int total_segs = TOTAL_SEGS(sbi); 337 unsigned int total_segs = TOTAL_SEGS(sbi);
@@ -318,6 +362,18 @@ void clear_prefree_segments(struct f2fs_sb_info *sbi)
318 GFP_NOFS, 0); 362 GFP_NOFS, 0);
319 } 363 }
320 mutex_unlock(&dirty_i->seglist_lock); 364 mutex_unlock(&dirty_i->seglist_lock);
365
366 /* send small discards */
367 list_for_each_safe(this, next, head) {
368 entry = list_entry(this, struct discard_entry, list);
369 blkdev_issue_discard(sbi->sb->s_bdev,
370 entry->blkaddr << sbi->log_sectors_per_block,
371 (1 << sbi->log_sectors_per_block) * entry->len,
372 GFP_NOFS, 0);
373 list_del(&entry->list);
374 SM_I(sbi)->nr_discards -= entry->len;
375 kmem_cache_free(discard_entry_slab, entry);
376 }
321} 377}
322 378
323static void __mark_sit_entry_dirty(struct f2fs_sb_info *sbi, unsigned int segno) 379static void __mark_sit_entry_dirty(struct f2fs_sb_info *sbi, unsigned int segno)
@@ -1469,6 +1525,10 @@ void flush_sit_entries(struct f2fs_sb_info *sbi)
1469 1525
1470 sit_offset = SIT_ENTRY_OFFSET(sit_i, segno); 1526 sit_offset = SIT_ENTRY_OFFSET(sit_i, segno);
1471 1527
1528 /* add discard candidates */
1529 if (SM_I(sbi)->nr_discards < SM_I(sbi)->max_discards)
1530 add_discard_addrs(sbi, segno, se);
1531
1472 if (flushed) 1532 if (flushed)
1473 goto to_sit_page; 1533 goto to_sit_page;
1474 1534