mirror of
				git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
				synced 2025-11-01 09:13:37 +00:00 
			
		
		
		
	f2fs: reduce unncessary locking pages during read
This patch reduces redundant locking and unlocking pages during read operations. In f2fs_readpage, let's use wait_on_page_locked() instead of lock_page. And then, when we need to modify any data finally, let's lock the page so that we can avoid lock contention. [readpage rule] - The f2fs_readpage returns unlocked page, or released page too in error cases. - Its caller should handle read error, -EIO, after locking the page, which indicates read completion. - Its caller should check PageUptodate after grab_cache_page. Signed-off-by: Changman Lee <cm224.lee@samsung.com> Reviewed-by: Namjae Jeon <namjae.jeon@samsung.com> Signed-off-by: Jaegeuk Kim <jaegeuk.kim@samsung.com>
This commit is contained in:
		
							parent
							
								
									25c0a6e529
								
							
						
					
					
						commit
						393ff91f57
					
				
					 4 changed files with 96 additions and 73 deletions
				
			
		| 
						 | 
				
			
			@ -57,13 +57,15 @@ repeat:
 | 
			
		|||
		cond_resched();
 | 
			
		||||
		goto repeat;
 | 
			
		||||
	}
 | 
			
		||||
	if (f2fs_readpage(sbi, page, index, READ_SYNC)) {
 | 
			
		||||
		f2fs_put_page(page, 1);
 | 
			
		||||
		goto repeat;
 | 
			
		||||
	}
 | 
			
		||||
	mark_page_accessed(page);
 | 
			
		||||
	if (PageUptodate(page))
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	/* We do not allow returning an errorneous page */
 | 
			
		||||
	if (f2fs_readpage(sbi, page, index, READ_SYNC))
 | 
			
		||||
		goto repeat;
 | 
			
		||||
 | 
			
		||||
	lock_page(page);
 | 
			
		||||
out:
 | 
			
		||||
	mark_page_accessed(page);
 | 
			
		||||
	return page;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -199,12 +199,17 @@ struct page *find_data_page(struct inode *inode, pgoff_t index)
 | 
			
		|||
	if (!page)
 | 
			
		||||
		return ERR_PTR(-ENOMEM);
 | 
			
		||||
 | 
			
		||||
	err = f2fs_readpage(sbi, page, dn.data_blkaddr, READ_SYNC);
 | 
			
		||||
	if (err) {
 | 
			
		||||
		f2fs_put_page(page, 1);
 | 
			
		||||
		return ERR_PTR(err);
 | 
			
		||||
	if (PageUptodate(page)) {
 | 
			
		||||
		unlock_page(page);
 | 
			
		||||
		return page;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = f2fs_readpage(sbi, page, dn.data_blkaddr, READ_SYNC);
 | 
			
		||||
	wait_on_page_locked(page);
 | 
			
		||||
	if (!PageUptodate(page)) {
 | 
			
		||||
		f2fs_put_page(page, 0);
 | 
			
		||||
		return ERR_PTR(-EIO);
 | 
			
		||||
	}
 | 
			
		||||
	unlock_page(page);
 | 
			
		||||
	return page;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -241,9 +246,13 @@ struct page *get_lock_data_page(struct inode *inode, pgoff_t index)
 | 
			
		|||
	BUG_ON(dn.data_blkaddr == NULL_ADDR);
 | 
			
		||||
 | 
			
		||||
	err = f2fs_readpage(sbi, page, dn.data_blkaddr, READ_SYNC);
 | 
			
		||||
	if (err) {
 | 
			
		||||
		f2fs_put_page(page, 1);
 | 
			
		||||
	if (err)
 | 
			
		||||
		return ERR_PTR(err);
 | 
			
		||||
 | 
			
		||||
	lock_page(page);
 | 
			
		||||
	if (!PageUptodate(page)) {
 | 
			
		||||
		f2fs_put_page(page, 1);
 | 
			
		||||
		return ERR_PTR(-EIO);
 | 
			
		||||
	}
 | 
			
		||||
	return page;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -283,14 +292,17 @@ struct page *get_new_data_page(struct inode *inode, pgoff_t index,
 | 
			
		|||
 | 
			
		||||
	if (dn.data_blkaddr == NEW_ADDR) {
 | 
			
		||||
		zero_user_segment(page, 0, PAGE_CACHE_SIZE);
 | 
			
		||||
		SetPageUptodate(page);
 | 
			
		||||
	} else {
 | 
			
		||||
		err = f2fs_readpage(sbi, page, dn.data_blkaddr, READ_SYNC);
 | 
			
		||||
		if (err) {
 | 
			
		||||
			f2fs_put_page(page, 1);
 | 
			
		||||
		if (err)
 | 
			
		||||
			return ERR_PTR(err);
 | 
			
		||||
		lock_page(page);
 | 
			
		||||
		if (!PageUptodate(page)) {
 | 
			
		||||
			f2fs_put_page(page, 1);
 | 
			
		||||
			return ERR_PTR(-EIO);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	SetPageUptodate(page);
 | 
			
		||||
 | 
			
		||||
	if (new_i_size &&
 | 
			
		||||
		i_size_read(inode) < ((index + 1) << PAGE_CACHE_SHIFT)) {
 | 
			
		||||
| 
						 | 
				
			
			@ -325,22 +337,14 @@ static void read_end_io(struct bio *bio, int err)
 | 
			
		|||
 | 
			
		||||
/*
 | 
			
		||||
 * Fill the locked page with data located in the block address.
 | 
			
		||||
 * Read operation is synchronous, and caller must unlock the page.
 | 
			
		||||
 * Return unlocked page.
 | 
			
		||||
 */
 | 
			
		||||
int f2fs_readpage(struct f2fs_sb_info *sbi, struct page *page,
 | 
			
		||||
					block_t blk_addr, int type)
 | 
			
		||||
{
 | 
			
		||||
	struct block_device *bdev = sbi->sb->s_bdev;
 | 
			
		||||
	bool sync = (type == READ_SYNC);
 | 
			
		||||
	struct bio *bio;
 | 
			
		||||
 | 
			
		||||
	/* This page can be already read by other threads */
 | 
			
		||||
	if (PageUptodate(page)) {
 | 
			
		||||
		if (!sync)
 | 
			
		||||
			unlock_page(page);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	down_read(&sbi->bio_sem);
 | 
			
		||||
 | 
			
		||||
	/* Allocate a new bio */
 | 
			
		||||
| 
						 | 
				
			
			@ -354,18 +358,12 @@ int f2fs_readpage(struct f2fs_sb_info *sbi, struct page *page,
 | 
			
		|||
		kfree(bio->bi_private);
 | 
			
		||||
		bio_put(bio);
 | 
			
		||||
		up_read(&sbi->bio_sem);
 | 
			
		||||
		f2fs_put_page(page, 1);
 | 
			
		||||
		return -EFAULT;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	submit_bio(type, bio);
 | 
			
		||||
	up_read(&sbi->bio_sem);
 | 
			
		||||
 | 
			
		||||
	/* wait for read completion if sync */
 | 
			
		||||
	if (sync) {
 | 
			
		||||
		lock_page(page);
 | 
			
		||||
		if (PageError(page))
 | 
			
		||||
			return -EIO;
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -636,18 +634,22 @@ static int f2fs_write_begin(struct file *file, struct address_space *mapping,
 | 
			
		|||
 | 
			
		||||
		/* Reading beyond i_size is simple: memset to zero */
 | 
			
		||||
		zero_user_segments(page, 0, start, end, PAGE_CACHE_SIZE);
 | 
			
		||||
		return 0;
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (dn.data_blkaddr == NEW_ADDR) {
 | 
			
		||||
		zero_user_segment(page, 0, PAGE_CACHE_SIZE);
 | 
			
		||||
	} else {
 | 
			
		||||
		err = f2fs_readpage(sbi, page, dn.data_blkaddr, READ_SYNC);
 | 
			
		||||
		if (err) {
 | 
			
		||||
			f2fs_put_page(page, 1);
 | 
			
		||||
		if (err)
 | 
			
		||||
			return err;
 | 
			
		||||
		lock_page(page);
 | 
			
		||||
		if (!PageUptodate(page)) {
 | 
			
		||||
			f2fs_put_page(page, 1);
 | 
			
		||||
			return -EIO;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
out:
 | 
			
		||||
	SetPageUptodate(page);
 | 
			
		||||
	clear_cold_data(page);
 | 
			
		||||
	return 0;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -100,10 +100,13 @@ static void ra_nat_pages(struct f2fs_sb_info *sbi, int nid)
 | 
			
		|||
		page = grab_cache_page(mapping, index);
 | 
			
		||||
		if (!page)
 | 
			
		||||
			continue;
 | 
			
		||||
		if (f2fs_readpage(sbi, page, index, READ)) {
 | 
			
		||||
		if (PageUptodate(page)) {
 | 
			
		||||
			f2fs_put_page(page, 1);
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		if (f2fs_readpage(sbi, page, index, READ))
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		f2fs_put_page(page, 0);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -851,8 +854,16 @@ static int read_node_page(struct page *page, int type)
 | 
			
		|||
 | 
			
		||||
	get_node_info(sbi, page->index, &ni);
 | 
			
		||||
 | 
			
		||||
	if (ni.blk_addr == NULL_ADDR)
 | 
			
		||||
	if (ni.blk_addr == NULL_ADDR) {
 | 
			
		||||
		f2fs_put_page(page, 1);
 | 
			
		||||
		return -ENOENT;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (PageUptodate(page)) {
 | 
			
		||||
		unlock_page(page);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return f2fs_readpage(sbi, page, ni.blk_addr, type);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -865,19 +876,18 @@ void ra_node_page(struct f2fs_sb_info *sbi, nid_t nid)
 | 
			
		|||
	struct page *apage;
 | 
			
		||||
 | 
			
		||||
	apage = find_get_page(mapping, nid);
 | 
			
		||||
	if (apage && PageUptodate(apage))
 | 
			
		||||
		goto release_out;
 | 
			
		||||
	if (apage && PageUptodate(apage)) {
 | 
			
		||||
		f2fs_put_page(apage, 0);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	f2fs_put_page(apage, 0);
 | 
			
		||||
 | 
			
		||||
	apage = grab_cache_page(mapping, nid);
 | 
			
		||||
	if (!apage)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (read_node_page(apage, READA))
 | 
			
		||||
		unlock_page(apage);
 | 
			
		||||
 | 
			
		||||
release_out:
 | 
			
		||||
	f2fs_put_page(apage, 0);
 | 
			
		||||
	if (read_node_page(apage, READA) == 0)
 | 
			
		||||
		f2fs_put_page(apage, 0);
 | 
			
		||||
	return;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -892,11 +902,14 @@ struct page *get_node_page(struct f2fs_sb_info *sbi, pgoff_t nid)
 | 
			
		|||
		return ERR_PTR(-ENOMEM);
 | 
			
		||||
 | 
			
		||||
	err = read_node_page(page, READ_SYNC);
 | 
			
		||||
	if (err) {
 | 
			
		||||
		f2fs_put_page(page, 1);
 | 
			
		||||
	if (err)
 | 
			
		||||
		return ERR_PTR(err);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	lock_page(page);
 | 
			
		||||
	if (!PageUptodate(page)) {
 | 
			
		||||
		f2fs_put_page(page, 1);
 | 
			
		||||
		return ERR_PTR(-EIO);
 | 
			
		||||
	}
 | 
			
		||||
	BUG_ON(nid != nid_of_node(page));
 | 
			
		||||
	mark_page_accessed(page);
 | 
			
		||||
	return page;
 | 
			
		||||
| 
						 | 
				
			
			@ -928,11 +941,8 @@ repeat:
 | 
			
		|||
		goto page_hit;
 | 
			
		||||
 | 
			
		||||
	err = read_node_page(page, READ_SYNC);
 | 
			
		||||
	unlock_page(page);
 | 
			
		||||
	if (err) {
 | 
			
		||||
		f2fs_put_page(page, 0);
 | 
			
		||||
	if (err)
 | 
			
		||||
		return ERR_PTR(err);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Then, try readahead for siblings of the desired node */
 | 
			
		||||
	end = start + MAX_RA_NODE;
 | 
			
		||||
| 
						 | 
				
			
			@ -957,6 +967,7 @@ page_hit:
 | 
			
		|||
		f2fs_put_page(page, 1);
 | 
			
		||||
		goto repeat;
 | 
			
		||||
	}
 | 
			
		||||
	mark_page_accessed(page);
 | 
			
		||||
	return page;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1473,23 +1484,24 @@ int restore_node_summary(struct f2fs_sb_info *sbi,
 | 
			
		|||
	sum_entry = &sum->entries[0];
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < last_offset; i++, sum_entry++) {
 | 
			
		||||
		if (f2fs_readpage(sbi, page, addr, READ_SYNC))
 | 
			
		||||
			goto out;
 | 
			
		||||
 | 
			
		||||
		rn = (struct f2fs_node *)page_address(page);
 | 
			
		||||
		sum_entry->nid = rn->footer.nid;
 | 
			
		||||
		sum_entry->version = 0;
 | 
			
		||||
		sum_entry->ofs_in_node = 0;
 | 
			
		||||
		addr++;
 | 
			
		||||
 | 
			
		||||
		/*
 | 
			
		||||
		 * In order to read next node page,
 | 
			
		||||
		 * we must clear PageUptodate flag.
 | 
			
		||||
		 */
 | 
			
		||||
		ClearPageUptodate(page);
 | 
			
		||||
 | 
			
		||||
		if (f2fs_readpage(sbi, page, addr, READ_SYNC))
 | 
			
		||||
			goto out;
 | 
			
		||||
 | 
			
		||||
		lock_page(page);
 | 
			
		||||
		rn = (struct f2fs_node *)page_address(page);
 | 
			
		||||
		sum_entry->nid = rn->footer.nid;
 | 
			
		||||
		sum_entry->version = 0;
 | 
			
		||||
		sum_entry->ofs_in_node = 0;
 | 
			
		||||
		addr++;
 | 
			
		||||
	}
 | 
			
		||||
out:
 | 
			
		||||
	unlock_page(page);
 | 
			
		||||
out:
 | 
			
		||||
	__free_pages(page, 0);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -112,11 +112,16 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head)
 | 
			
		|||
	while (1) {
 | 
			
		||||
		struct fsync_inode_entry *entry;
 | 
			
		||||
 | 
			
		||||
		if (f2fs_readpage(sbi, page, blkaddr, READ_SYNC))
 | 
			
		||||
		err = f2fs_readpage(sbi, page, blkaddr, READ_SYNC);
 | 
			
		||||
		if (err)
 | 
			
		||||
			goto out;
 | 
			
		||||
 | 
			
		||||
		if (cp_ver != cpver_of_node(page))
 | 
			
		||||
			goto out;
 | 
			
		||||
		lock_page(page);
 | 
			
		||||
 | 
			
		||||
		if (cp_ver != cpver_of_node(page)) {
 | 
			
		||||
			err = -EINVAL;
 | 
			
		||||
			goto unlock_out;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (!is_fsync_dnode(page))
 | 
			
		||||
			goto next;
 | 
			
		||||
| 
						 | 
				
			
			@ -131,7 +136,7 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head)
 | 
			
		|||
			if (IS_INODE(page) && is_dent_dnode(page)) {
 | 
			
		||||
				if (recover_inode_page(sbi, page)) {
 | 
			
		||||
					err = -ENOMEM;
 | 
			
		||||
					goto out;
 | 
			
		||||
					goto unlock_out;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -139,14 +144,14 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head)
 | 
			
		|||
			entry = kmem_cache_alloc(fsync_entry_slab, GFP_NOFS);
 | 
			
		||||
			if (!entry) {
 | 
			
		||||
				err = -ENOMEM;
 | 
			
		||||
				goto out;
 | 
			
		||||
				goto unlock_out;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			entry->inode = f2fs_iget(sbi->sb, ino_of_node(page));
 | 
			
		||||
			if (IS_ERR(entry->inode)) {
 | 
			
		||||
				err = PTR_ERR(entry->inode);
 | 
			
		||||
				kmem_cache_free(fsync_entry_slab, entry);
 | 
			
		||||
				goto out;
 | 
			
		||||
				goto unlock_out;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			list_add_tail(&entry->list, head);
 | 
			
		||||
| 
						 | 
				
			
			@ -155,15 +160,15 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head)
 | 
			
		|||
		if (IS_INODE(page)) {
 | 
			
		||||
			err = recover_inode(entry->inode, page);
 | 
			
		||||
			if (err)
 | 
			
		||||
				goto out;
 | 
			
		||||
				goto unlock_out;
 | 
			
		||||
		}
 | 
			
		||||
next:
 | 
			
		||||
		/* check next segment */
 | 
			
		||||
		blkaddr = next_blkaddr_of_node(page);
 | 
			
		||||
		ClearPageUptodate(page);
 | 
			
		||||
	}
 | 
			
		||||
out:
 | 
			
		||||
unlock_out:
 | 
			
		||||
	unlock_page(page);
 | 
			
		||||
out:
 | 
			
		||||
	__free_pages(page, 0);
 | 
			
		||||
	return err;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -319,8 +324,10 @@ static void recover_data(struct f2fs_sb_info *sbi,
 | 
			
		|||
		if (f2fs_readpage(sbi, page, blkaddr, READ_SYNC))
 | 
			
		||||
			goto out;
 | 
			
		||||
 | 
			
		||||
		lock_page(page);
 | 
			
		||||
 | 
			
		||||
		if (cp_ver != cpver_of_node(page))
 | 
			
		||||
			goto out;
 | 
			
		||||
			goto unlock_out;
 | 
			
		||||
 | 
			
		||||
		entry = get_fsync_inode(head, ino_of_node(page));
 | 
			
		||||
		if (!entry)
 | 
			
		||||
| 
						 | 
				
			
			@ -336,10 +343,10 @@ static void recover_data(struct f2fs_sb_info *sbi,
 | 
			
		|||
next:
 | 
			
		||||
		/* check next segment */
 | 
			
		||||
		blkaddr = next_blkaddr_of_node(page);
 | 
			
		||||
		ClearPageUptodate(page);
 | 
			
		||||
	}
 | 
			
		||||
out:
 | 
			
		||||
unlock_out:
 | 
			
		||||
	unlock_page(page);
 | 
			
		||||
out:
 | 
			
		||||
	__free_pages(page, 0);
 | 
			
		||||
 | 
			
		||||
	allocate_new_segments(sbi);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue