aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYan, Zheng <zheng.z.yan@intel.com>2012-05-28 02:44:30 -0400
committerAlex Elder <elder@dreamhost.com>2012-06-01 09:06:29 -0400
commit28c0254ede13ab575d2df5c6585ed3d4817c3e6b (patch)
treeec4ea075748189c9be942fb41c70691d21b4d2df
parent6bd9adbdf9ca6a052b0b7455ac67b925eb38cfad (diff)
ceph: check PG_Private flag before accessing page->private
I got lots of NULL pointer dereference Oops when compiling kernel on ceph. The bug is because the kernel page migration routine replaces some pages in the page cache with new pages, these new pages' private can be non-zero. Signed-off-by: Zheng Yan <zheng.z.yan@intel.com> Signed-off-by: Sage Weil <sage@inktank.com>
-rw-r--r--fs/ceph/addr.c21
1 files changed, 12 insertions, 9 deletions
diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c
index 173b1d22e59b..8b67304e4b80 100644
--- a/fs/ceph/addr.c
+++ b/fs/ceph/addr.c
@@ -54,7 +54,12 @@
54 (CONGESTION_ON_THRESH(congestion_kb) - \ 54 (CONGESTION_ON_THRESH(congestion_kb) - \
55 (CONGESTION_ON_THRESH(congestion_kb) >> 2)) 55 (CONGESTION_ON_THRESH(congestion_kb) >> 2))
56 56
57 57static inline struct ceph_snap_context *page_snap_context(struct page *page)
58{
59 if (PagePrivate(page))
60 return (void *)page->private;
61 return NULL;
62}
58 63
59/* 64/*
60 * Dirty a page. Optimistically adjust accounting, on the assumption 65 * Dirty a page. Optimistically adjust accounting, on the assumption
@@ -142,10 +147,9 @@ static void ceph_invalidatepage(struct page *page, unsigned long offset)
142{ 147{
143 struct inode *inode; 148 struct inode *inode;
144 struct ceph_inode_info *ci; 149 struct ceph_inode_info *ci;
145 struct ceph_snap_context *snapc = (void *)page->private; 150 struct ceph_snap_context *snapc = page_snap_context(page);
146 151
147 BUG_ON(!PageLocked(page)); 152 BUG_ON(!PageLocked(page));
148 BUG_ON(!page->private);
149 BUG_ON(!PagePrivate(page)); 153 BUG_ON(!PagePrivate(page));
150 BUG_ON(!page->mapping); 154 BUG_ON(!page->mapping);
151 155
@@ -182,7 +186,6 @@ static int ceph_releasepage(struct page *page, gfp_t g)
182 struct inode *inode = page->mapping ? page->mapping->host : NULL; 186 struct inode *inode = page->mapping ? page->mapping->host : NULL;
183 dout("%p releasepage %p idx %lu\n", inode, page, page->index); 187 dout("%p releasepage %p idx %lu\n", inode, page, page->index);
184 WARN_ON(PageDirty(page)); 188 WARN_ON(PageDirty(page));
185 WARN_ON(page->private);
186 WARN_ON(PagePrivate(page)); 189 WARN_ON(PagePrivate(page));
187 return 0; 190 return 0;
188} 191}
@@ -443,7 +446,7 @@ static int writepage_nounlock(struct page *page, struct writeback_control *wbc)
443 osdc = &fsc->client->osdc; 446 osdc = &fsc->client->osdc;
444 447
445 /* verify this is a writeable snap context */ 448 /* verify this is a writeable snap context */
446 snapc = (void *)page->private; 449 snapc = page_snap_context(page);
447 if (snapc == NULL) { 450 if (snapc == NULL) {
448 dout("writepage %p page %p not dirty?\n", inode, page); 451 dout("writepage %p page %p not dirty?\n", inode, page);
449 goto out; 452 goto out;
@@ -451,7 +454,7 @@ static int writepage_nounlock(struct page *page, struct writeback_control *wbc)
451 oldest = get_oldest_context(inode, &snap_size); 454 oldest = get_oldest_context(inode, &snap_size);
452 if (snapc->seq > oldest->seq) { 455 if (snapc->seq > oldest->seq) {
453 dout("writepage %p page %p snapc %p not writeable - noop\n", 456 dout("writepage %p page %p snapc %p not writeable - noop\n",
454 inode, page, (void *)page->private); 457 inode, page, snapc);
455 /* we should only noop if called by kswapd */ 458 /* we should only noop if called by kswapd */
456 WARN_ON((current->flags & PF_MEMALLOC) == 0); 459 WARN_ON((current->flags & PF_MEMALLOC) == 0);
457 ceph_put_snap_context(oldest); 460 ceph_put_snap_context(oldest);
@@ -591,7 +594,7 @@ static void writepages_finish(struct ceph_osd_request *req,
591 clear_bdi_congested(&fsc->backing_dev_info, 594 clear_bdi_congested(&fsc->backing_dev_info,
592 BLK_RW_ASYNC); 595 BLK_RW_ASYNC);
593 596
594 ceph_put_snap_context((void *)page->private); 597 ceph_put_snap_context(page_snap_context(page));
595 page->private = 0; 598 page->private = 0;
596 ClearPagePrivate(page); 599 ClearPagePrivate(page);
597 dout("unlocking %d %p\n", i, page); 600 dout("unlocking %d %p\n", i, page);
@@ -795,7 +798,7 @@ get_more_pages:
795 } 798 }
796 799
797 /* only if matching snap context */ 800 /* only if matching snap context */
798 pgsnapc = (void *)page->private; 801 pgsnapc = page_snap_context(page);
799 if (pgsnapc->seq > snapc->seq) { 802 if (pgsnapc->seq > snapc->seq) {
800 dout("page snapc %p %lld > oldest %p %lld\n", 803 dout("page snapc %p %lld > oldest %p %lld\n",
801 pgsnapc, pgsnapc->seq, snapc, snapc->seq); 804 pgsnapc, pgsnapc->seq, snapc, snapc->seq);
@@ -984,7 +987,7 @@ retry_locked:
984 BUG_ON(!ci->i_snap_realm); 987 BUG_ON(!ci->i_snap_realm);
985 down_read(&mdsc->snap_rwsem); 988 down_read(&mdsc->snap_rwsem);
986 BUG_ON(!ci->i_snap_realm->cached_context); 989 BUG_ON(!ci->i_snap_realm->cached_context);
987 snapc = (void *)page->private; 990 snapc = page_snap_context(page);
988 if (snapc && snapc != ci->i_head_snapc) { 991 if (snapc && snapc != ci->i_head_snapc) {
989 /* 992 /*
990 * this page is already dirty in another (older) snap 993 * this page is already dirty in another (older) snap