mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	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>
This commit is contained in:
		
							parent
							
								
									7fd9e544fb
								
							
						
					
					
						commit
						b29555505d
					
				
					 1 changed files with 60 additions and 0 deletions
				
			
		| 
						 | 
				
			
			@ -266,6 +266,47 @@ static void locate_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno)
 | 
			
		|||
	mutex_unlock(&dirty_i->seglist_lock);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void add_discard_addrs(struct f2fs_sb_info *sbi,
 | 
			
		||||
			unsigned int segno, struct seg_entry *se)
 | 
			
		||||
{
 | 
			
		||||
	struct list_head *head = &SM_I(sbi)->discard_list;
 | 
			
		||||
	struct discard_entry *new;
 | 
			
		||||
	int entries = SIT_VBLOCK_MAP_SIZE / sizeof(unsigned long);
 | 
			
		||||
	int max_blocks = sbi->blocks_per_seg;
 | 
			
		||||
	unsigned long *cur_map = (unsigned long *)se->cur_valid_map;
 | 
			
		||||
	unsigned long *ckpt_map = (unsigned long *)se->ckpt_valid_map;
 | 
			
		||||
	unsigned long dmap[entries];
 | 
			
		||||
	unsigned int start = 0, end = -1;
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	if (!test_opt(sbi, DISCARD))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	/* zero block will be discarded through the prefree list */
 | 
			
		||||
	if (!se->valid_blocks || se->valid_blocks == max_blocks)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	/* SIT_VBLOCK_MAP_SIZE should be multiple of sizeof(unsigned long) */
 | 
			
		||||
	for (i = 0; i < entries; i++)
 | 
			
		||||
		dmap[i] = (cur_map[i] ^ ckpt_map[i]) & ckpt_map[i];
 | 
			
		||||
 | 
			
		||||
	while (SM_I(sbi)->nr_discards <= SM_I(sbi)->max_discards) {
 | 
			
		||||
		start = __find_rev_next_bit(dmap, max_blocks, end + 1);
 | 
			
		||||
		if (start >= max_blocks)
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		end = __find_rev_next_zero_bit(dmap, max_blocks, start + 1);
 | 
			
		||||
 | 
			
		||||
		new = f2fs_kmem_cache_alloc(discard_entry_slab, GFP_NOFS);
 | 
			
		||||
		INIT_LIST_HEAD(&new->list);
 | 
			
		||||
		new->blkaddr = START_BLOCK(sbi, segno) + start;
 | 
			
		||||
		new->len = end - start;
 | 
			
		||||
 | 
			
		||||
		list_add_tail(&new->list, head);
 | 
			
		||||
		SM_I(sbi)->nr_discards += end - start;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Should call clear_prefree_segments after checkpoint is done.
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			@ -288,6 +329,9 @@ 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 dirty_seglist_info *dirty_i = DIRTY_I(sbi);
 | 
			
		||||
	unsigned long *prefree_map = dirty_i->dirty_segmap[PRE];
 | 
			
		||||
	unsigned int total_segs = TOTAL_SEGS(sbi);
 | 
			
		||||
| 
						 | 
				
			
			@ -318,6 +362,18 @@ void clear_prefree_segments(struct f2fs_sb_info *sbi)
 | 
			
		|||
				GFP_NOFS, 0);
 | 
			
		||||
	}
 | 
			
		||||
	mutex_unlock(&dirty_i->seglist_lock);
 | 
			
		||||
 | 
			
		||||
	/* send small discards */
 | 
			
		||||
	list_for_each_safe(this, next, head) {
 | 
			
		||||
		entry = list_entry(this, struct discard_entry, list);
 | 
			
		||||
		blkdev_issue_discard(sbi->sb->s_bdev,
 | 
			
		||||
				entry->blkaddr << sbi->log_sectors_per_block,
 | 
			
		||||
				(1 << sbi->log_sectors_per_block) * entry->len,
 | 
			
		||||
				GFP_NOFS, 0);
 | 
			
		||||
		list_del(&entry->list);
 | 
			
		||||
		SM_I(sbi)->nr_discards -= entry->len;
 | 
			
		||||
		kmem_cache_free(discard_entry_slab, entry);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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)
 | 
			
		|||
 | 
			
		||||
		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 (flushed)
 | 
			
		||||
			goto to_sit_page;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue