aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorSage Weil <sage@newdream.net>2010-04-01 00:52:10 -0400
committerSage Weil <sage@newdream.net>2010-04-01 12:34:36 -0400
commit80e755fedebc8de0599a79efad2c656503df2e62 (patch)
tree05f0285f4d040958377d6542394bc56b10f80257 /fs
parent9358c6d4c0264b1572554c49c4b92673ea9a5c72 (diff)
ceph: allow writeback of snapped pages older than 'oldest' snapc
On snap deletion, we don't regenerate ceph_cap_snaps for inodes with dirty pages because deletion does not affect metadata writeback. However, we did run into problems when we went to write back the pages because the 'oldest' snapc is determined by the oldest cap_snap, and that may be the newer snapc that reflects the deletion. This caused confusion and an infinite loop in ceph_update_writeable_page(). Change the snapc checks to allow writeback of any snapc that is equal to OR older than the 'oldest' snapc. When there are no cap_snaps, we were also using the realm's latest snapc for writeback, which complicates ceph_put_wrbufffer_cap_refs(). Instead, use i_head_snapc, the most snapc used for the most recent ('head') data. This makes the writeback snapc (ceph_osd_request.r_snapc) _always_ match a capsnap or i_head_snapc. Also, in writepags_finish(), drop the snapc referenced by the _page_ and do not assume it matches the request snapc (it may not anymore). Signed-off-by: Sage Weil <sage@newdream.net>
Diffstat (limited to 'fs')
-rw-r--r--fs/ceph/addr.c27
1 files changed, 14 insertions, 13 deletions
diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c
index ce8ef6107727..a313e9baeed0 100644
--- a/fs/ceph/addr.c
+++ b/fs/ceph/addr.c
@@ -356,8 +356,8 @@ static struct ceph_snap_context *__get_oldest_context(struct inode *inode,
356 break; 356 break;
357 } 357 }
358 } 358 }
359 if (!snapc && ci->i_snap_realm) { 359 if (!snapc && ci->i_head_snapc) {
360 snapc = ceph_get_snap_context(ci->i_snap_realm->cached_context); 360 snapc = ceph_get_snap_context(ci->i_head_snapc);
361 dout(" head snapc %p has %d dirty pages\n", 361 dout(" head snapc %p has %d dirty pages\n",
362 snapc, ci->i_wrbuffer_ref_head); 362 snapc, ci->i_wrbuffer_ref_head);
363 } 363 }
@@ -412,7 +412,7 @@ static int writepage_nounlock(struct page *page, struct writeback_control *wbc)
412 dout("writepage %p page %p not dirty?\n", inode, page); 412 dout("writepage %p page %p not dirty?\n", inode, page);
413 goto out; 413 goto out;
414 } 414 }
415 if (snapc != get_oldest_context(inode, &snap_size)) { 415 if (snapc->seq > get_oldest_context(inode, &snap_size)->seq) {
416 dout("writepage %p page %p snapc %p not writeable - noop\n", 416 dout("writepage %p page %p snapc %p not writeable - noop\n",
417 inode, page, (void *)page->private); 417 inode, page, (void *)page->private);
418 /* we should only noop if called by kswapd */ 418 /* we should only noop if called by kswapd */
@@ -557,9 +557,9 @@ static void writepages_finish(struct ceph_osd_request *req,
557 dout("inode %p skipping page %p\n", inode, page); 557 dout("inode %p skipping page %p\n", inode, page);
558 wbc->pages_skipped++; 558 wbc->pages_skipped++;
559 } 559 }
560 ceph_put_snap_context((void *)page->private);
560 page->private = 0; 561 page->private = 0;
561 ClearPagePrivate(page); 562 ClearPagePrivate(page);
562 ceph_put_snap_context(snapc);
563 dout("unlocking %d %p\n", i, page); 563 dout("unlocking %d %p\n", i, page);
564 end_page_writeback(page); 564 end_page_writeback(page);
565 565
@@ -617,7 +617,7 @@ static int ceph_writepages_start(struct address_space *mapping,
617 int range_whole = 0; 617 int range_whole = 0;
618 int should_loop = 1; 618 int should_loop = 1;
619 pgoff_t max_pages = 0, max_pages_ever = 0; 619 pgoff_t max_pages = 0, max_pages_ever = 0;
620 struct ceph_snap_context *snapc = NULL, *last_snapc = NULL; 620 struct ceph_snap_context *snapc = NULL, *last_snapc = NULL, *pgsnapc;
621 struct pagevec pvec; 621 struct pagevec pvec;
622 int done = 0; 622 int done = 0;
623 int rc = 0; 623 int rc = 0;
@@ -769,9 +769,10 @@ get_more_pages:
769 } 769 }
770 770
771 /* only if matching snap context */ 771 /* only if matching snap context */
772 if (snapc != (void *)page->private) { 772 pgsnapc = (void *)page->private;
773 dout("page snapc %p != oldest %p\n", 773 if (pgsnapc->seq > snapc->seq) {
774 (void *)page->private, snapc); 774 dout("page snapc %p %lld > oldest %p %lld\n",
775 pgsnapc, pgsnapc->seq, snapc, snapc->seq);
775 unlock_page(page); 776 unlock_page(page);
776 if (!locked_pages) 777 if (!locked_pages)
777 continue; /* keep looking for snap */ 778 continue; /* keep looking for snap */
@@ -935,8 +936,8 @@ static int ceph_update_writeable_page(struct file *file,
935 int pos_in_page = pos & ~PAGE_CACHE_MASK; 936 int pos_in_page = pos & ~PAGE_CACHE_MASK;
936 int end_in_page = pos_in_page + len; 937 int end_in_page = pos_in_page + len;
937 loff_t i_size; 938 loff_t i_size;
938 struct ceph_snap_context *snapc;
939 int r; 939 int r;
940 struct ceph_snap_context *snapc, *oldest;
940 941
941retry_locked: 942retry_locked:
942 /* writepages currently holds page lock, but if we change that later, */ 943 /* writepages currently holds page lock, but if we change that later, */
@@ -946,16 +947,16 @@ retry_locked:
946 BUG_ON(!ci->i_snap_realm); 947 BUG_ON(!ci->i_snap_realm);
947 down_read(&mdsc->snap_rwsem); 948 down_read(&mdsc->snap_rwsem);
948 BUG_ON(!ci->i_snap_realm->cached_context); 949 BUG_ON(!ci->i_snap_realm->cached_context);
949 if (page->private && 950 snapc = (void *)page->private;
950 (void *)page->private != ci->i_snap_realm->cached_context) { 951 if (snapc && snapc != ci->i_head_snapc) {
951 /* 952 /*
952 * this page is already dirty in another (older) snap 953 * this page is already dirty in another (older) snap
953 * context! is it writeable now? 954 * context! is it writeable now?
954 */ 955 */
955 snapc = get_oldest_context(inode, NULL); 956 oldest = get_oldest_context(inode, NULL);
956 up_read(&mdsc->snap_rwsem); 957 up_read(&mdsc->snap_rwsem);
957 958
958 if (snapc != (void *)page->private) { 959 if (snapc->seq > oldest->seq) {
959 dout(" page %p snapc %p not current or oldest\n", 960 dout(" page %p snapc %p not current or oldest\n",
960 page, (void *)page->private); 961 page, (void *)page->private);
961 /* 962 /*