diff options
author | Jaegeuk Kim <jaegeuk.kim@samsung.com> | 2013-03-08 07:29:23 -0500 |
---|---|---|
committer | Jaegeuk Kim <jaegeuk.kim@samsung.com> | 2013-03-20 05:30:06 -0400 |
commit | 393ff91f57c87d48ffed30878be6e3e486d3a00a (patch) | |
tree | c80fe33bcf8546ebce9ab6fc043b99889e67536f | |
parent | 25c0a6e529b56ca010e1f46239edd07c1b484b63 (diff) |
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>
-rw-r--r-- | fs/f2fs/checkpoint.c | 12 | ||||
-rw-r--r-- | fs/f2fs/data.c | 58 | ||||
-rw-r--r-- | fs/f2fs/node.c | 58 | ||||
-rw-r--r-- | fs/f2fs/recovery.c | 31 |
4 files changed, 91 insertions, 68 deletions
diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 2b6fc131e2ce..d947e66ee8a8 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c | |||
@@ -57,13 +57,15 @@ repeat: | |||
57 | cond_resched(); | 57 | cond_resched(); |
58 | goto repeat; | 58 | goto repeat; |
59 | } | 59 | } |
60 | if (f2fs_readpage(sbi, page, index, READ_SYNC)) { | 60 | if (PageUptodate(page)) |
61 | f2fs_put_page(page, 1); | 61 | goto out; |
62 | |||
63 | if (f2fs_readpage(sbi, page, index, READ_SYNC)) | ||
62 | goto repeat; | 64 | goto repeat; |
63 | } | ||
64 | mark_page_accessed(page); | ||
65 | 65 | ||
66 | /* We do not allow returning an errorneous page */ | 66 | lock_page(page); |
67 | out: | ||
68 | mark_page_accessed(page); | ||
67 | return page; | 69 | return page; |
68 | } | 70 | } |
69 | 71 | ||
diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 277966a8547a..c8e20b618913 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c | |||
@@ -199,12 +199,17 @@ struct page *find_data_page(struct inode *inode, pgoff_t index) | |||
199 | if (!page) | 199 | if (!page) |
200 | return ERR_PTR(-ENOMEM); | 200 | return ERR_PTR(-ENOMEM); |
201 | 201 | ||
202 | if (PageUptodate(page)) { | ||
203 | unlock_page(page); | ||
204 | return page; | ||
205 | } | ||
206 | |||
202 | err = f2fs_readpage(sbi, page, dn.data_blkaddr, READ_SYNC); | 207 | err = f2fs_readpage(sbi, page, dn.data_blkaddr, READ_SYNC); |
203 | if (err) { | 208 | wait_on_page_locked(page); |
204 | f2fs_put_page(page, 1); | 209 | if (!PageUptodate(page)) { |
205 | return ERR_PTR(err); | 210 | f2fs_put_page(page, 0); |
211 | return ERR_PTR(-EIO); | ||
206 | } | 212 | } |
207 | unlock_page(page); | ||
208 | return page; | 213 | return page; |
209 | } | 214 | } |
210 | 215 | ||
@@ -241,9 +246,13 @@ struct page *get_lock_data_page(struct inode *inode, pgoff_t index) | |||
241 | BUG_ON(dn.data_blkaddr == NULL_ADDR); | 246 | BUG_ON(dn.data_blkaddr == NULL_ADDR); |
242 | 247 | ||
243 | err = f2fs_readpage(sbi, page, dn.data_blkaddr, READ_SYNC); | 248 | err = f2fs_readpage(sbi, page, dn.data_blkaddr, READ_SYNC); |
244 | if (err) { | 249 | if (err) |
245 | f2fs_put_page(page, 1); | ||
246 | return ERR_PTR(err); | 250 | return ERR_PTR(err); |
251 | |||
252 | lock_page(page); | ||
253 | if (!PageUptodate(page)) { | ||
254 | f2fs_put_page(page, 1); | ||
255 | return ERR_PTR(-EIO); | ||
247 | } | 256 | } |
248 | return page; | 257 | return page; |
249 | } | 258 | } |
@@ -283,14 +292,17 @@ struct page *get_new_data_page(struct inode *inode, pgoff_t index, | |||
283 | 292 | ||
284 | if (dn.data_blkaddr == NEW_ADDR) { | 293 | if (dn.data_blkaddr == NEW_ADDR) { |
285 | zero_user_segment(page, 0, PAGE_CACHE_SIZE); | 294 | zero_user_segment(page, 0, PAGE_CACHE_SIZE); |
295 | SetPageUptodate(page); | ||
286 | } else { | 296 | } else { |
287 | err = f2fs_readpage(sbi, page, dn.data_blkaddr, READ_SYNC); | 297 | err = f2fs_readpage(sbi, page, dn.data_blkaddr, READ_SYNC); |
288 | if (err) { | 298 | if (err) |
289 | f2fs_put_page(page, 1); | ||
290 | return ERR_PTR(err); | 299 | return ERR_PTR(err); |
300 | lock_page(page); | ||
301 | if (!PageUptodate(page)) { | ||
302 | f2fs_put_page(page, 1); | ||
303 | return ERR_PTR(-EIO); | ||
291 | } | 304 | } |
292 | } | 305 | } |
293 | SetPageUptodate(page); | ||
294 | 306 | ||
295 | if (new_i_size && | 307 | if (new_i_size && |
296 | i_size_read(inode) < ((index + 1) << PAGE_CACHE_SHIFT)) { | 308 | i_size_read(inode) < ((index + 1) << PAGE_CACHE_SHIFT)) { |
@@ -325,22 +337,14 @@ static void read_end_io(struct bio *bio, int err) | |||
325 | 337 | ||
326 | /* | 338 | /* |
327 | * Fill the locked page with data located in the block address. | 339 | * Fill the locked page with data located in the block address. |
328 | * Read operation is synchronous, and caller must unlock the page. | 340 | * Return unlocked page. |
329 | */ | 341 | */ |
330 | int f2fs_readpage(struct f2fs_sb_info *sbi, struct page *page, | 342 | int f2fs_readpage(struct f2fs_sb_info *sbi, struct page *page, |
331 | block_t blk_addr, int type) | 343 | block_t blk_addr, int type) |
332 | { | 344 | { |
333 | struct block_device *bdev = sbi->sb->s_bdev; | 345 | struct block_device *bdev = sbi->sb->s_bdev; |
334 | bool sync = (type == READ_SYNC); | ||
335 | struct bio *bio; | 346 | struct bio *bio; |
336 | 347 | ||
337 | /* This page can be already read by other threads */ | ||
338 | if (PageUptodate(page)) { | ||
339 | if (!sync) | ||
340 | unlock_page(page); | ||
341 | return 0; | ||
342 | } | ||
343 | |||
344 | down_read(&sbi->bio_sem); | 348 | down_read(&sbi->bio_sem); |
345 | 349 | ||
346 | /* Allocate a new bio */ | 350 | /* Allocate a new bio */ |
@@ -354,18 +358,12 @@ int f2fs_readpage(struct f2fs_sb_info *sbi, struct page *page, | |||
354 | kfree(bio->bi_private); | 358 | kfree(bio->bi_private); |
355 | bio_put(bio); | 359 | bio_put(bio); |
356 | up_read(&sbi->bio_sem); | 360 | up_read(&sbi->bio_sem); |
361 | f2fs_put_page(page, 1); | ||
357 | return -EFAULT; | 362 | return -EFAULT; |
358 | } | 363 | } |
359 | 364 | ||
360 | submit_bio(type, bio); | 365 | submit_bio(type, bio); |
361 | up_read(&sbi->bio_sem); | 366 | up_read(&sbi->bio_sem); |
362 | |||
363 | /* wait for read completion if sync */ | ||
364 | if (sync) { | ||
365 | lock_page(page); | ||
366 | if (PageError(page)) | ||
367 | return -EIO; | ||
368 | } | ||
369 | return 0; | 367 | return 0; |
370 | } | 368 | } |
371 | 369 | ||
@@ -636,18 +634,22 @@ static int f2fs_write_begin(struct file *file, struct address_space *mapping, | |||
636 | 634 | ||
637 | /* Reading beyond i_size is simple: memset to zero */ | 635 | /* Reading beyond i_size is simple: memset to zero */ |
638 | zero_user_segments(page, 0, start, end, PAGE_CACHE_SIZE); | 636 | zero_user_segments(page, 0, start, end, PAGE_CACHE_SIZE); |
639 | return 0; | 637 | goto out; |
640 | } | 638 | } |
641 | 639 | ||
642 | if (dn.data_blkaddr == NEW_ADDR) { | 640 | if (dn.data_blkaddr == NEW_ADDR) { |
643 | zero_user_segment(page, 0, PAGE_CACHE_SIZE); | 641 | zero_user_segment(page, 0, PAGE_CACHE_SIZE); |
644 | } else { | 642 | } else { |
645 | err = f2fs_readpage(sbi, page, dn.data_blkaddr, READ_SYNC); | 643 | err = f2fs_readpage(sbi, page, dn.data_blkaddr, READ_SYNC); |
646 | if (err) { | 644 | if (err) |
647 | f2fs_put_page(page, 1); | ||
648 | return err; | 645 | return err; |
646 | lock_page(page); | ||
647 | if (!PageUptodate(page)) { | ||
648 | f2fs_put_page(page, 1); | ||
649 | return -EIO; | ||
649 | } | 650 | } |
650 | } | 651 | } |
652 | out: | ||
651 | SetPageUptodate(page); | 653 | SetPageUptodate(page); |
652 | clear_cold_data(page); | 654 | clear_cold_data(page); |
653 | return 0; | 655 | return 0; |
diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index a3cb1ff34f8e..9e6ed6708fa8 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c | |||
@@ -100,10 +100,13 @@ static void ra_nat_pages(struct f2fs_sb_info *sbi, int nid) | |||
100 | page = grab_cache_page(mapping, index); | 100 | page = grab_cache_page(mapping, index); |
101 | if (!page) | 101 | if (!page) |
102 | continue; | 102 | continue; |
103 | if (f2fs_readpage(sbi, page, index, READ)) { | 103 | if (PageUptodate(page)) { |
104 | f2fs_put_page(page, 1); | 104 | f2fs_put_page(page, 1); |
105 | continue; | 105 | continue; |
106 | } | 106 | } |
107 | if (f2fs_readpage(sbi, page, index, READ)) | ||
108 | continue; | ||
109 | |||
107 | f2fs_put_page(page, 0); | 110 | f2fs_put_page(page, 0); |
108 | } | 111 | } |
109 | } | 112 | } |
@@ -851,8 +854,16 @@ static int read_node_page(struct page *page, int type) | |||
851 | 854 | ||
852 | get_node_info(sbi, page->index, &ni); | 855 | get_node_info(sbi, page->index, &ni); |
853 | 856 | ||
854 | if (ni.blk_addr == NULL_ADDR) | 857 | if (ni.blk_addr == NULL_ADDR) { |
858 | f2fs_put_page(page, 1); | ||
855 | return -ENOENT; | 859 | return -ENOENT; |
860 | } | ||
861 | |||
862 | if (PageUptodate(page)) { | ||
863 | unlock_page(page); | ||
864 | return 0; | ||
865 | } | ||
866 | |||
856 | return f2fs_readpage(sbi, page, ni.blk_addr, type); | 867 | return f2fs_readpage(sbi, page, ni.blk_addr, type); |
857 | } | 868 | } |
858 | 869 | ||
@@ -865,19 +876,18 @@ void ra_node_page(struct f2fs_sb_info *sbi, nid_t nid) | |||
865 | struct page *apage; | 876 | struct page *apage; |
866 | 877 | ||
867 | apage = find_get_page(mapping, nid); | 878 | apage = find_get_page(mapping, nid); |
868 | if (apage && PageUptodate(apage)) | 879 | if (apage && PageUptodate(apage)) { |
869 | goto release_out; | 880 | f2fs_put_page(apage, 0); |
881 | return; | ||
882 | } | ||
870 | f2fs_put_page(apage, 0); | 883 | f2fs_put_page(apage, 0); |
871 | 884 | ||
872 | apage = grab_cache_page(mapping, nid); | 885 | apage = grab_cache_page(mapping, nid); |
873 | if (!apage) | 886 | if (!apage) |
874 | return; | 887 | return; |
875 | 888 | ||
876 | if (read_node_page(apage, READA)) | 889 | if (read_node_page(apage, READA) == 0) |
877 | unlock_page(apage); | 890 | f2fs_put_page(apage, 0); |
878 | |||
879 | release_out: | ||
880 | f2fs_put_page(apage, 0); | ||
881 | return; | 891 | return; |
882 | } | 892 | } |
883 | 893 | ||
@@ -892,11 +902,14 @@ struct page *get_node_page(struct f2fs_sb_info *sbi, pgoff_t nid) | |||
892 | return ERR_PTR(-ENOMEM); | 902 | return ERR_PTR(-ENOMEM); |
893 | 903 | ||
894 | err = read_node_page(page, READ_SYNC); | 904 | err = read_node_page(page, READ_SYNC); |
895 | if (err) { | 905 | if (err) |
896 | f2fs_put_page(page, 1); | ||
897 | return ERR_PTR(err); | 906 | return ERR_PTR(err); |
898 | } | ||
899 | 907 | ||
908 | lock_page(page); | ||
909 | if (!PageUptodate(page)) { | ||
910 | f2fs_put_page(page, 1); | ||
911 | return ERR_PTR(-EIO); | ||
912 | } | ||
900 | BUG_ON(nid != nid_of_node(page)); | 913 | BUG_ON(nid != nid_of_node(page)); |
901 | mark_page_accessed(page); | 914 | mark_page_accessed(page); |
902 | return page; | 915 | return page; |
@@ -928,11 +941,8 @@ repeat: | |||
928 | goto page_hit; | 941 | goto page_hit; |
929 | 942 | ||
930 | err = read_node_page(page, READ_SYNC); | 943 | err = read_node_page(page, READ_SYNC); |
931 | unlock_page(page); | 944 | if (err) |
932 | if (err) { | ||
933 | f2fs_put_page(page, 0); | ||
934 | return ERR_PTR(err); | 945 | return ERR_PTR(err); |
935 | } | ||
936 | 946 | ||
937 | /* Then, try readahead for siblings of the desired node */ | 947 | /* Then, try readahead for siblings of the desired node */ |
938 | end = start + MAX_RA_NODE; | 948 | end = start + MAX_RA_NODE; |
@@ -957,6 +967,7 @@ page_hit: | |||
957 | f2fs_put_page(page, 1); | 967 | f2fs_put_page(page, 1); |
958 | goto repeat; | 968 | goto repeat; |
959 | } | 969 | } |
970 | mark_page_accessed(page); | ||
960 | return page; | 971 | return page; |
961 | } | 972 | } |
962 | 973 | ||
@@ -1473,23 +1484,24 @@ int restore_node_summary(struct f2fs_sb_info *sbi, | |||
1473 | sum_entry = &sum->entries[0]; | 1484 | sum_entry = &sum->entries[0]; |
1474 | 1485 | ||
1475 | for (i = 0; i < last_offset; i++, sum_entry++) { | 1486 | for (i = 0; i < last_offset; i++, sum_entry++) { |
1487 | /* | ||
1488 | * In order to read next node page, | ||
1489 | * we must clear PageUptodate flag. | ||
1490 | */ | ||
1491 | ClearPageUptodate(page); | ||
1492 | |||
1476 | if (f2fs_readpage(sbi, page, addr, READ_SYNC)) | 1493 | if (f2fs_readpage(sbi, page, addr, READ_SYNC)) |
1477 | goto out; | 1494 | goto out; |
1478 | 1495 | ||
1496 | lock_page(page); | ||
1479 | rn = (struct f2fs_node *)page_address(page); | 1497 | rn = (struct f2fs_node *)page_address(page); |
1480 | sum_entry->nid = rn->footer.nid; | 1498 | sum_entry->nid = rn->footer.nid; |
1481 | sum_entry->version = 0; | 1499 | sum_entry->version = 0; |
1482 | sum_entry->ofs_in_node = 0; | 1500 | sum_entry->ofs_in_node = 0; |
1483 | addr++; | 1501 | addr++; |
1484 | |||
1485 | /* | ||
1486 | * In order to read next node page, | ||
1487 | * we must clear PageUptodate flag. | ||
1488 | */ | ||
1489 | ClearPageUptodate(page); | ||
1490 | } | 1502 | } |
1491 | out: | ||
1492 | unlock_page(page); | 1503 | unlock_page(page); |
1504 | out: | ||
1493 | __free_pages(page, 0); | 1505 | __free_pages(page, 0); |
1494 | return 0; | 1506 | return 0; |
1495 | } | 1507 | } |
diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index 6b82e2034cfd..2d86eb26c493 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c | |||
@@ -112,11 +112,16 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head) | |||
112 | while (1) { | 112 | while (1) { |
113 | struct fsync_inode_entry *entry; | 113 | struct fsync_inode_entry *entry; |
114 | 114 | ||
115 | if (f2fs_readpage(sbi, page, blkaddr, READ_SYNC)) | 115 | err = f2fs_readpage(sbi, page, blkaddr, READ_SYNC); |
116 | if (err) | ||
116 | goto out; | 117 | goto out; |
117 | 118 | ||
118 | if (cp_ver != cpver_of_node(page)) | 119 | lock_page(page); |
119 | goto out; | 120 | |
121 | if (cp_ver != cpver_of_node(page)) { | ||
122 | err = -EINVAL; | ||
123 | goto unlock_out; | ||
124 | } | ||
120 | 125 | ||
121 | if (!is_fsync_dnode(page)) | 126 | if (!is_fsync_dnode(page)) |
122 | goto next; | 127 | goto next; |
@@ -131,7 +136,7 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head) | |||
131 | if (IS_INODE(page) && is_dent_dnode(page)) { | 136 | if (IS_INODE(page) && is_dent_dnode(page)) { |
132 | if (recover_inode_page(sbi, page)) { | 137 | if (recover_inode_page(sbi, page)) { |
133 | err = -ENOMEM; | 138 | err = -ENOMEM; |
134 | goto out; | 139 | goto unlock_out; |
135 | } | 140 | } |
136 | } | 141 | } |
137 | 142 | ||
@@ -139,14 +144,14 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head) | |||
139 | entry = kmem_cache_alloc(fsync_entry_slab, GFP_NOFS); | 144 | entry = kmem_cache_alloc(fsync_entry_slab, GFP_NOFS); |
140 | if (!entry) { | 145 | if (!entry) { |
141 | err = -ENOMEM; | 146 | err = -ENOMEM; |
142 | goto out; | 147 | goto unlock_out; |
143 | } | 148 | } |
144 | 149 | ||
145 | entry->inode = f2fs_iget(sbi->sb, ino_of_node(page)); | 150 | entry->inode = f2fs_iget(sbi->sb, ino_of_node(page)); |
146 | if (IS_ERR(entry->inode)) { | 151 | if (IS_ERR(entry->inode)) { |
147 | err = PTR_ERR(entry->inode); | 152 | err = PTR_ERR(entry->inode); |
148 | kmem_cache_free(fsync_entry_slab, entry); | 153 | kmem_cache_free(fsync_entry_slab, entry); |
149 | goto out; | 154 | goto unlock_out; |
150 | } | 155 | } |
151 | 156 | ||
152 | list_add_tail(&entry->list, head); | 157 | 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 | if (IS_INODE(page)) { | 160 | if (IS_INODE(page)) { |
156 | err = recover_inode(entry->inode, page); | 161 | err = recover_inode(entry->inode, page); |
157 | if (err) | 162 | if (err) |
158 | goto out; | 163 | goto unlock_out; |
159 | } | 164 | } |
160 | next: | 165 | next: |
161 | /* check next segment */ | 166 | /* check next segment */ |
162 | blkaddr = next_blkaddr_of_node(page); | 167 | blkaddr = next_blkaddr_of_node(page); |
163 | ClearPageUptodate(page); | ||
164 | } | 168 | } |
165 | out: | 169 | unlock_out: |
166 | unlock_page(page); | 170 | unlock_page(page); |
171 | out: | ||
167 | __free_pages(page, 0); | 172 | __free_pages(page, 0); |
168 | return err; | 173 | return err; |
169 | } | 174 | } |
@@ -319,8 +324,10 @@ static void recover_data(struct f2fs_sb_info *sbi, | |||
319 | if (f2fs_readpage(sbi, page, blkaddr, READ_SYNC)) | 324 | if (f2fs_readpage(sbi, page, blkaddr, READ_SYNC)) |
320 | goto out; | 325 | goto out; |
321 | 326 | ||
327 | lock_page(page); | ||
328 | |||
322 | if (cp_ver != cpver_of_node(page)) | 329 | if (cp_ver != cpver_of_node(page)) |
323 | goto out; | 330 | goto unlock_out; |
324 | 331 | ||
325 | entry = get_fsync_inode(head, ino_of_node(page)); | 332 | entry = get_fsync_inode(head, ino_of_node(page)); |
326 | if (!entry) | 333 | if (!entry) |
@@ -336,10 +343,10 @@ static void recover_data(struct f2fs_sb_info *sbi, | |||
336 | next: | 343 | next: |
337 | /* check next segment */ | 344 | /* check next segment */ |
338 | blkaddr = next_blkaddr_of_node(page); | 345 | blkaddr = next_blkaddr_of_node(page); |
339 | ClearPageUptodate(page); | ||
340 | } | 346 | } |
341 | out: | 347 | unlock_out: |
342 | unlock_page(page); | 348 | unlock_page(page); |
349 | out: | ||
343 | __free_pages(page, 0); | 350 | __free_pages(page, 0); |
344 | 351 | ||
345 | allocate_new_segments(sbi); | 352 | allocate_new_segments(sbi); |