Commit 393ff91f authored by Jaegeuk Kim's avatar Jaegeuk Kim

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: default avatarChangman Lee <cm224.lee@samsung.com>
Reviewed-by: default avatarNamjae Jeon <namjae.jeon@samsung.com>
Signed-off-by: default avatarJaegeuk Kim <jaegeuk.kim@samsung.com>
parent 25c0a6e5
...@@ -57,13 +57,15 @@ struct page *get_meta_page(struct f2fs_sb_info *sbi, pgoff_t index) ...@@ -57,13 +57,15 @@ struct page *get_meta_page(struct f2fs_sb_info *sbi, pgoff_t index)
cond_resched(); cond_resched();
goto repeat; goto repeat;
} }
if (f2fs_readpage(sbi, page, index, READ_SYNC)) { if (PageUptodate(page))
f2fs_put_page(page, 1); goto out;
if (f2fs_readpage(sbi, page, index, READ_SYNC))
goto repeat; goto repeat;
}
mark_page_accessed(page);
/* We do not allow returning an errorneous page */ lock_page(page);
out:
mark_page_accessed(page);
return page; return page;
} }
......
...@@ -199,12 +199,17 @@ struct page *find_data_page(struct inode *inode, pgoff_t index) ...@@ -199,12 +199,17 @@ struct page *find_data_page(struct inode *inode, pgoff_t index)
if (!page) if (!page)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
if (PageUptodate(page)) {
unlock_page(page);
return page;
}
err = f2fs_readpage(sbi, page, dn.data_blkaddr, READ_SYNC); err = f2fs_readpage(sbi, page, dn.data_blkaddr, READ_SYNC);
if (err) { wait_on_page_locked(page);
f2fs_put_page(page, 1); if (!PageUptodate(page)) {
return ERR_PTR(err); f2fs_put_page(page, 0);
return ERR_PTR(-EIO);
} }
unlock_page(page);
return page; return page;
} }
...@@ -241,9 +246,13 @@ struct page *get_lock_data_page(struct inode *inode, pgoff_t index) ...@@ -241,9 +246,13 @@ struct page *get_lock_data_page(struct inode *inode, pgoff_t index)
BUG_ON(dn.data_blkaddr == NULL_ADDR); BUG_ON(dn.data_blkaddr == NULL_ADDR);
err = f2fs_readpage(sbi, page, dn.data_blkaddr, READ_SYNC); err = f2fs_readpage(sbi, page, dn.data_blkaddr, READ_SYNC);
if (err) { if (err)
f2fs_put_page(page, 1);
return ERR_PTR(err); return ERR_PTR(err);
lock_page(page);
if (!PageUptodate(page)) {
f2fs_put_page(page, 1);
return ERR_PTR(-EIO);
} }
return page; return page;
} }
...@@ -283,14 +292,17 @@ struct page *get_new_data_page(struct inode *inode, pgoff_t index, ...@@ -283,14 +292,17 @@ struct page *get_new_data_page(struct inode *inode, pgoff_t index,
if (dn.data_blkaddr == NEW_ADDR) { if (dn.data_blkaddr == NEW_ADDR) {
zero_user_segment(page, 0, PAGE_CACHE_SIZE); zero_user_segment(page, 0, PAGE_CACHE_SIZE);
SetPageUptodate(page);
} else { } else {
err = f2fs_readpage(sbi, page, dn.data_blkaddr, READ_SYNC); err = f2fs_readpage(sbi, page, dn.data_blkaddr, READ_SYNC);
if (err) { if (err)
f2fs_put_page(page, 1);
return ERR_PTR(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 && if (new_i_size &&
i_size_read(inode) < ((index + 1) << PAGE_CACHE_SHIFT)) { i_size_read(inode) < ((index + 1) << PAGE_CACHE_SHIFT)) {
...@@ -325,22 +337,14 @@ static void read_end_io(struct bio *bio, int err) ...@@ -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. * 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, int f2fs_readpage(struct f2fs_sb_info *sbi, struct page *page,
block_t blk_addr, int type) block_t blk_addr, int type)
{ {
struct block_device *bdev = sbi->sb->s_bdev; struct block_device *bdev = sbi->sb->s_bdev;
bool sync = (type == READ_SYNC);
struct bio *bio; 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); down_read(&sbi->bio_sem);
/* Allocate a new bio */ /* Allocate a new bio */
...@@ -354,18 +358,12 @@ int f2fs_readpage(struct f2fs_sb_info *sbi, struct page *page, ...@@ -354,18 +358,12 @@ int f2fs_readpage(struct f2fs_sb_info *sbi, struct page *page,
kfree(bio->bi_private); kfree(bio->bi_private);
bio_put(bio); bio_put(bio);
up_read(&sbi->bio_sem); up_read(&sbi->bio_sem);
f2fs_put_page(page, 1);
return -EFAULT; return -EFAULT;
} }
submit_bio(type, bio); submit_bio(type, bio);
up_read(&sbi->bio_sem); up_read(&sbi->bio_sem);
/* wait for read completion if sync */
if (sync) {
lock_page(page);
if (PageError(page))
return -EIO;
}
return 0; return 0;
} }
...@@ -636,18 +634,22 @@ static int f2fs_write_begin(struct file *file, struct address_space *mapping, ...@@ -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 */ /* Reading beyond i_size is simple: memset to zero */
zero_user_segments(page, 0, start, end, PAGE_CACHE_SIZE); zero_user_segments(page, 0, start, end, PAGE_CACHE_SIZE);
return 0; goto out;
} }
if (dn.data_blkaddr == NEW_ADDR) { if (dn.data_blkaddr == NEW_ADDR) {
zero_user_segment(page, 0, PAGE_CACHE_SIZE); zero_user_segment(page, 0, PAGE_CACHE_SIZE);
} else { } else {
err = f2fs_readpage(sbi, page, dn.data_blkaddr, READ_SYNC); err = f2fs_readpage(sbi, page, dn.data_blkaddr, READ_SYNC);
if (err) { if (err)
f2fs_put_page(page, 1);
return err; return err;
lock_page(page);
if (!PageUptodate(page)) {
f2fs_put_page(page, 1);
return -EIO;
} }
} }
out:
SetPageUptodate(page); SetPageUptodate(page);
clear_cold_data(page); clear_cold_data(page);
return 0; return 0;
......
...@@ -100,10 +100,13 @@ static void ra_nat_pages(struct f2fs_sb_info *sbi, int nid) ...@@ -100,10 +100,13 @@ static void ra_nat_pages(struct f2fs_sb_info *sbi, int nid)
page = grab_cache_page(mapping, index); page = grab_cache_page(mapping, index);
if (!page) if (!page)
continue; continue;
if (f2fs_readpage(sbi, page, index, READ)) { if (PageUptodate(page)) {
f2fs_put_page(page, 1); f2fs_put_page(page, 1);
continue; continue;
} }
if (f2fs_readpage(sbi, page, index, READ))
continue;
f2fs_put_page(page, 0); f2fs_put_page(page, 0);
} }
} }
...@@ -851,8 +854,16 @@ static int read_node_page(struct page *page, int type) ...@@ -851,8 +854,16 @@ static int read_node_page(struct page *page, int type)
get_node_info(sbi, page->index, &ni); 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; return -ENOENT;
}
if (PageUptodate(page)) {
unlock_page(page);
return 0;
}
return f2fs_readpage(sbi, page, ni.blk_addr, type); return f2fs_readpage(sbi, page, ni.blk_addr, type);
} }
...@@ -865,18 +876,17 @@ void ra_node_page(struct f2fs_sb_info *sbi, nid_t nid) ...@@ -865,18 +876,17 @@ void ra_node_page(struct f2fs_sb_info *sbi, nid_t nid)
struct page *apage; struct page *apage;
apage = find_get_page(mapping, nid); apage = find_get_page(mapping, nid);
if (apage && PageUptodate(apage)) if (apage && PageUptodate(apage)) {
goto release_out; f2fs_put_page(apage, 0);
return;
}
f2fs_put_page(apage, 0); f2fs_put_page(apage, 0);
apage = grab_cache_page(mapping, nid); apage = grab_cache_page(mapping, nid);
if (!apage) if (!apage)
return; return;
if (read_node_page(apage, READA)) if (read_node_page(apage, READA) == 0)
unlock_page(apage);
release_out:
f2fs_put_page(apage, 0); f2fs_put_page(apage, 0);
return; return;
} }
...@@ -892,11 +902,14 @@ struct page *get_node_page(struct f2fs_sb_info *sbi, pgoff_t nid) ...@@ -892,11 +902,14 @@ struct page *get_node_page(struct f2fs_sb_info *sbi, pgoff_t nid)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
err = read_node_page(page, READ_SYNC); err = read_node_page(page, READ_SYNC);
if (err) { if (err)
f2fs_put_page(page, 1);
return ERR_PTR(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)); BUG_ON(nid != nid_of_node(page));
mark_page_accessed(page); mark_page_accessed(page);
return page; return page;
...@@ -928,11 +941,8 @@ struct page *get_node_page_ra(struct page *parent, int start) ...@@ -928,11 +941,8 @@ struct page *get_node_page_ra(struct page *parent, int start)
goto page_hit; goto page_hit;
err = read_node_page(page, READ_SYNC); err = read_node_page(page, READ_SYNC);
unlock_page(page); if (err)
if (err) {
f2fs_put_page(page, 0);
return ERR_PTR(err); return ERR_PTR(err);
}
/* Then, try readahead for siblings of the desired node */ /* Then, try readahead for siblings of the desired node */
end = start + MAX_RA_NODE; end = start + MAX_RA_NODE;
...@@ -957,6 +967,7 @@ struct page *get_node_page_ra(struct page *parent, int start) ...@@ -957,6 +967,7 @@ struct page *get_node_page_ra(struct page *parent, int start)
f2fs_put_page(page, 1); f2fs_put_page(page, 1);
goto repeat; goto repeat;
} }
mark_page_accessed(page);
return page; return page;
} }
...@@ -1473,23 +1484,24 @@ int restore_node_summary(struct f2fs_sb_info *sbi, ...@@ -1473,23 +1484,24 @@ int restore_node_summary(struct f2fs_sb_info *sbi,
sum_entry = &sum->entries[0]; sum_entry = &sum->entries[0];
for (i = 0; i < last_offset; i++, sum_entry++) { for (i = 0; i < last_offset; i++, sum_entry++) {
/*
* In order to read next node page,
* we must clear PageUptodate flag.
*/
ClearPageUptodate(page);
if (f2fs_readpage(sbi, page, addr, READ_SYNC)) if (f2fs_readpage(sbi, page, addr, READ_SYNC))
goto out; goto out;
lock_page(page);
rn = (struct f2fs_node *)page_address(page); rn = (struct f2fs_node *)page_address(page);
sum_entry->nid = rn->footer.nid; sum_entry->nid = rn->footer.nid;
sum_entry->version = 0; sum_entry->version = 0;
sum_entry->ofs_in_node = 0; sum_entry->ofs_in_node = 0;
addr++; addr++;
/*
* In order to read next node page,
* we must clear PageUptodate flag.
*/
ClearPageUptodate(page);
} }
out:
unlock_page(page); unlock_page(page);
out:
__free_pages(page, 0); __free_pages(page, 0);
return 0; return 0;
} }
......
...@@ -112,11 +112,16 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head) ...@@ -112,11 +112,16 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head)
while (1) { while (1) {
struct fsync_inode_entry *entry; 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; goto out;
if (cp_ver != cpver_of_node(page)) lock_page(page);
goto out;
if (cp_ver != cpver_of_node(page)) {
err = -EINVAL;
goto unlock_out;
}
if (!is_fsync_dnode(page)) if (!is_fsync_dnode(page))
goto next; goto next;
...@@ -131,7 +136,7 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head) ...@@ -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 (IS_INODE(page) && is_dent_dnode(page)) {
if (recover_inode_page(sbi, page)) { if (recover_inode_page(sbi, page)) {
err = -ENOMEM; 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) ...@@ -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); entry = kmem_cache_alloc(fsync_entry_slab, GFP_NOFS);
if (!entry) { if (!entry) {
err = -ENOMEM; err = -ENOMEM;
goto out; goto unlock_out;
} }
entry->inode = f2fs_iget(sbi->sb, ino_of_node(page)); entry->inode = f2fs_iget(sbi->sb, ino_of_node(page));
if (IS_ERR(entry->inode)) { if (IS_ERR(entry->inode)) {
err = PTR_ERR(entry->inode); err = PTR_ERR(entry->inode);
kmem_cache_free(fsync_entry_slab, entry); kmem_cache_free(fsync_entry_slab, entry);
goto out; goto unlock_out;
} }
list_add_tail(&entry->list, head); list_add_tail(&entry->list, head);
...@@ -155,15 +160,15 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head) ...@@ -155,15 +160,15 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head)
if (IS_INODE(page)) { if (IS_INODE(page)) {
err = recover_inode(entry->inode, page); err = recover_inode(entry->inode, page);
if (err) if (err)
goto out; goto unlock_out;
} }
next: next:
/* check next segment */ /* check next segment */
blkaddr = next_blkaddr_of_node(page); blkaddr = next_blkaddr_of_node(page);
ClearPageUptodate(page);
} }
out: unlock_out:
unlock_page(page); unlock_page(page);
out:
__free_pages(page, 0); __free_pages(page, 0);
return err; return err;
} }
...@@ -319,8 +324,10 @@ static void recover_data(struct f2fs_sb_info *sbi, ...@@ -319,8 +324,10 @@ static void recover_data(struct f2fs_sb_info *sbi,
if (f2fs_readpage(sbi, page, blkaddr, READ_SYNC)) if (f2fs_readpage(sbi, page, blkaddr, READ_SYNC))
goto out; goto out;
lock_page(page);
if (cp_ver != cpver_of_node(page)) if (cp_ver != cpver_of_node(page))
goto out; goto unlock_out;
entry = get_fsync_inode(head, ino_of_node(page)); entry = get_fsync_inode(head, ino_of_node(page));
if (!entry) if (!entry)
...@@ -336,10 +343,10 @@ static void recover_data(struct f2fs_sb_info *sbi, ...@@ -336,10 +343,10 @@ static void recover_data(struct f2fs_sb_info *sbi,
next: next:
/* check next segment */ /* check next segment */
blkaddr = next_blkaddr_of_node(page); blkaddr = next_blkaddr_of_node(page);
ClearPageUptodate(page);
} }
out: unlock_out:
unlock_page(page); unlock_page(page);
out:
__free_pages(page, 0); __free_pages(page, 0);
allocate_new_segments(sbi); allocate_new_segments(sbi);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment