diff options
author | Jaegeuk Kim <jaegeuk.kim@samsung.com> | 2013-11-12 00:49:56 -0500 |
---|---|---|
committer | Jaegeuk Kim <jaegeuk.kim@samsung.com> | 2013-12-22 20:18:00 -0500 |
commit | b29555505d81e496fdbd125190c8043f3c09a83c (patch) | |
tree | c0043de3cbcea1f447c3414c1a5e63e007f2da2e /fs/f2fs/segment.c | |
parent | 7fd9e544fbb10c6ae4b4953f6063560c8eeae6e8 (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.c | 60 |
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 | ||
269 | static 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 | ||
289 | void clear_prefree_segments(struct f2fs_sb_info *sbi) | 330 | void 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 | ||
323 | static void __mark_sit_entry_dirty(struct f2fs_sb_info *sbi, unsigned int segno) | 379 | static 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 | ||