aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYan, Zheng <zyan@redhat.com>2017-09-01 22:50:48 -0400
committerIlya Dryomov <idryomov@gmail.com>2017-09-06 13:56:56 -0400
commit05455e1177f76849e0a6450e8710dcb2c361f337 (patch)
tree36e9f53c9a7715c3dafd72e2981933d45f42f0c3
parent1f934b00e907527cddb83984d0783cc4a029952a (diff)
ceph: make writepage_nounlock() invalidate page that beyonds EOF
Otherwise, the page left in state that page is associated with a snapc, but (PageDirty(page) || PageWriteback(page)) is false. Signed-off-by: "Yan, Zheng" <zyan@redhat.com> Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
-rw-r--r--fs/ceph/addr.c50
1 files changed, 32 insertions, 18 deletions
diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c
index 03a1ee27b33c..8526359c08b2 100644
--- a/fs/ceph/addr.c
+++ b/fs/ceph/addr.c
@@ -476,7 +476,8 @@ struct ceph_writeback_ctl
476 * only snap context we are allowed to write back. 476 * only snap context we are allowed to write back.
477 */ 477 */
478static struct ceph_snap_context * 478static struct ceph_snap_context *
479get_oldest_context(struct inode *inode, struct ceph_writeback_ctl *ctl) 479get_oldest_context(struct inode *inode, struct ceph_writeback_ctl *ctl,
480 struct ceph_snap_context *page_snapc)
480{ 481{
481 struct ceph_inode_info *ci = ceph_inode(inode); 482 struct ceph_inode_info *ci = ceph_inode(inode);
482 struct ceph_snap_context *snapc = NULL; 483 struct ceph_snap_context *snapc = NULL;
@@ -486,21 +487,33 @@ get_oldest_context(struct inode *inode, struct ceph_writeback_ctl *ctl)
486 list_for_each_entry(capsnap, &ci->i_cap_snaps, ci_item) { 487 list_for_each_entry(capsnap, &ci->i_cap_snaps, ci_item) {
487 dout(" cap_snap %p snapc %p has %d dirty pages\n", capsnap, 488 dout(" cap_snap %p snapc %p has %d dirty pages\n", capsnap,
488 capsnap->context, capsnap->dirty_pages); 489 capsnap->context, capsnap->dirty_pages);
489 if (capsnap->dirty_pages) { 490 if (!capsnap->dirty_pages)
490 snapc = ceph_get_snap_context(capsnap->context); 491 continue;
491 if (ctl) { 492
492 if (capsnap->writing) { 493 /* get i_size, truncate_{seq,size} for page_snapc? */
493 ctl->i_size = i_size_read(inode); 494 if (snapc && capsnap->context != page_snapc)
494 ctl->size_stable = false; 495 continue;
495 } else { 496
496 ctl->i_size = capsnap->size; 497 if (ctl) {
497 ctl->size_stable = true; 498 if (capsnap->writing) {
498 } 499 ctl->i_size = i_size_read(inode);
499 ctl->truncate_size = capsnap->truncate_size; 500 ctl->size_stable = false;
500 ctl->truncate_seq = capsnap->truncate_seq; 501 } else {
502 ctl->i_size = capsnap->size;
503 ctl->size_stable = true;
501 } 504 }
502 break; 505 ctl->truncate_size = capsnap->truncate_size;
506 ctl->truncate_seq = capsnap->truncate_seq;
503 } 507 }
508
509 if (snapc)
510 break;
511
512 snapc = ceph_get_snap_context(capsnap->context);
513 if (!page_snapc ||
514 page_snapc == snapc ||
515 page_snapc->seq > snapc->seq)
516 break;
504 } 517 }
505 if (!snapc && ci->i_wrbuffer_ref_head) { 518 if (!snapc && ci->i_wrbuffer_ref_head) {
506 snapc = ceph_get_snap_context(ci->i_head_snapc); 519 snapc = ceph_get_snap_context(ci->i_head_snapc);
@@ -573,7 +586,7 @@ static int writepage_nounlock(struct page *page, struct writeback_control *wbc)
573 dout("writepage %p page %p not dirty?\n", inode, page); 586 dout("writepage %p page %p not dirty?\n", inode, page);
574 return 0; 587 return 0;
575 } 588 }
576 oldest = get_oldest_context(inode, &ceph_wbc); 589 oldest = get_oldest_context(inode, &ceph_wbc, snapc);
577 if (snapc->seq > oldest->seq) { 590 if (snapc->seq > oldest->seq) {
578 dout("writepage %p page %p snapc %p not writeable - noop\n", 591 dout("writepage %p page %p snapc %p not writeable - noop\n",
579 inode, page, snapc); 592 inode, page, snapc);
@@ -588,6 +601,7 @@ static int writepage_nounlock(struct page *page, struct writeback_control *wbc)
588 /* is this a partial page at end of file? */ 601 /* is this a partial page at end of file? */
589 if (page_off >= ceph_wbc.i_size) { 602 if (page_off >= ceph_wbc.i_size) {
590 dout("%p page eof %llu\n", page, ceph_wbc.i_size); 603 dout("%p page eof %llu\n", page, ceph_wbc.i_size);
604 page->mapping->a_ops->invalidatepage(page, 0, PAGE_SIZE);
591 return 0; 605 return 0;
592 } 606 }
593 607
@@ -816,7 +830,7 @@ static int ceph_writepages_start(struct address_space *mapping,
816retry: 830retry:
817 /* find oldest snap context with dirty data */ 831 /* find oldest snap context with dirty data */
818 ceph_put_snap_context(snapc); 832 ceph_put_snap_context(snapc);
819 snapc = get_oldest_context(inode, &ceph_wbc); 833 snapc = get_oldest_context(inode, &ceph_wbc, NULL);
820 if (!snapc) { 834 if (!snapc) {
821 /* hmm, why does writepages get called when there 835 /* hmm, why does writepages get called when there
822 is no dirty data? */ 836 is no dirty data? */
@@ -1162,7 +1176,7 @@ out:
1162static int context_is_writeable_or_written(struct inode *inode, 1176static int context_is_writeable_or_written(struct inode *inode,
1163 struct ceph_snap_context *snapc) 1177 struct ceph_snap_context *snapc)
1164{ 1178{
1165 struct ceph_snap_context *oldest = get_oldest_context(inode, NULL); 1179 struct ceph_snap_context *oldest = get_oldest_context(inode, NULL, NULL);
1166 int ret = !oldest || snapc->seq <= oldest->seq; 1180 int ret = !oldest || snapc->seq <= oldest->seq;
1167 1181
1168 ceph_put_snap_context(oldest); 1182 ceph_put_snap_context(oldest);
@@ -1207,7 +1221,7 @@ retry_locked:
1207 * this page is already dirty in another (older) snap 1221 * this page is already dirty in another (older) snap
1208 * context! is it writeable now? 1222 * context! is it writeable now?
1209 */ 1223 */
1210 oldest = get_oldest_context(inode, NULL); 1224 oldest = get_oldest_context(inode, NULL, NULL);
1211 if (snapc->seq > oldest->seq) { 1225 if (snapc->seq > oldest->seq) {
1212 ceph_put_snap_context(oldest); 1226 ceph_put_snap_context(oldest);
1213 dout(" page %p snapc %p not current or oldest\n", 1227 dout(" page %p snapc %p not current or oldest\n",