diff options
author | Sage Weil <sage@newdream.net> | 2010-08-24 11:44:16 -0400 |
---|---|---|
committer | Sage Weil <sage@newdream.net> | 2010-08-24 19:24:18 -0400 |
commit | 7d8cb26d7dcb911f110b7762bd5941e8f009d6c3 (patch) | |
tree | 2adf2f6303cc96ff14c951dc6966f68a0fc3cf25 | |
parent | 07a27e226d1ed210d2d4218bd0642b40f5405c6a (diff) |
ceph: maintain i_head_snapc when any caps are dirty, not just for data
We used to use i_head_snapc to keep track of which snapc the current epoch
of dirty data was dirtied under. It is used by queue_cap_snap to set up
the cap_snap. However, since we queue cap snaps for any dirty caps, not
just for dirty file data, we need to keep a valid i_head_snapc anytime
we have dirty|flushing caps. This fixes a NULL pointer deref in
queue_cap_snap when writing back dirty caps without data (e.g.,
snaptest-authwb.sh).
Signed-off-by: Sage Weil <sage@newdream.net>
-rw-r--r-- | fs/ceph/addr.c | 4 | ||||
-rw-r--r-- | fs/ceph/caps.c | 20 | ||||
-rw-r--r-- | fs/ceph/snap.c | 6 | ||||
-rw-r--r-- | fs/ceph/super.h | 3 |
4 files changed, 26 insertions, 7 deletions
diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c index 420d46974ec8..4cfce1ee31fa 100644 --- a/fs/ceph/addr.c +++ b/fs/ceph/addr.c | |||
@@ -87,7 +87,7 @@ static int ceph_set_page_dirty(struct page *page) | |||
87 | 87 | ||
88 | /* dirty the head */ | 88 | /* dirty the head */ |
89 | spin_lock(&inode->i_lock); | 89 | spin_lock(&inode->i_lock); |
90 | if (ci->i_wrbuffer_ref_head == 0) | 90 | if (ci->i_head_snapc == NULL) |
91 | ci->i_head_snapc = ceph_get_snap_context(snapc); | 91 | ci->i_head_snapc = ceph_get_snap_context(snapc); |
92 | ++ci->i_wrbuffer_ref_head; | 92 | ++ci->i_wrbuffer_ref_head; |
93 | if (ci->i_wrbuffer_ref == 0) | 93 | if (ci->i_wrbuffer_ref == 0) |
@@ -346,7 +346,7 @@ static struct ceph_snap_context *get_oldest_context(struct inode *inode, | |||
346 | break; | 346 | break; |
347 | } | 347 | } |
348 | } | 348 | } |
349 | if (!snapc && ci->i_head_snapc) { | 349 | if (!snapc && ci->i_wrbuffer_ref_head) { |
350 | snapc = ceph_get_snap_context(ci->i_head_snapc); | 350 | snapc = ceph_get_snap_context(ci->i_head_snapc); |
351 | dout(" head snapc %p has %d dirty pages\n", | 351 | dout(" head snapc %p has %d dirty pages\n", |
352 | snapc, ci->i_wrbuffer_ref_head); | 352 | snapc, ci->i_wrbuffer_ref_head); |
diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index ba5bbf318fe1..a2069b6680ae 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c | |||
@@ -1143,6 +1143,10 @@ static int __send_cap(struct ceph_mds_client *mdsc, struct ceph_cap *cap, | |||
1143 | for (i = 0; i < CEPH_CAP_BITS; i++) | 1143 | for (i = 0; i < CEPH_CAP_BITS; i++) |
1144 | if (flushing & (1 << i)) | 1144 | if (flushing & (1 << i)) |
1145 | ci->i_cap_flush_tid[i] = flush_tid; | 1145 | ci->i_cap_flush_tid[i] = flush_tid; |
1146 | |||
1147 | follows = ci->i_head_snapc->seq; | ||
1148 | } else { | ||
1149 | follows = 0; | ||
1146 | } | 1150 | } |
1147 | 1151 | ||
1148 | keep = cap->implemented; | 1152 | keep = cap->implemented; |
@@ -1156,7 +1160,6 @@ static int __send_cap(struct ceph_mds_client *mdsc, struct ceph_cap *cap, | |||
1156 | mtime = inode->i_mtime; | 1160 | mtime = inode->i_mtime; |
1157 | atime = inode->i_atime; | 1161 | atime = inode->i_atime; |
1158 | time_warp_seq = ci->i_time_warp_seq; | 1162 | time_warp_seq = ci->i_time_warp_seq; |
1159 | follows = ci->i_snap_realm->cached_context->seq; | ||
1160 | uid = inode->i_uid; | 1163 | uid = inode->i_uid; |
1161 | gid = inode->i_gid; | 1164 | gid = inode->i_gid; |
1162 | mode = inode->i_mode; | 1165 | mode = inode->i_mode; |
@@ -1332,7 +1335,11 @@ void __ceph_mark_dirty_caps(struct ceph_inode_info *ci, int mask) | |||
1332 | ceph_cap_string(was | mask)); | 1335 | ceph_cap_string(was | mask)); |
1333 | ci->i_dirty_caps |= mask; | 1336 | ci->i_dirty_caps |= mask; |
1334 | if (was == 0) { | 1337 | if (was == 0) { |
1335 | dout(" inode %p now dirty\n", &ci->vfs_inode); | 1338 | if (!ci->i_head_snapc) |
1339 | ci->i_head_snapc = ceph_get_snap_context( | ||
1340 | ci->i_snap_realm->cached_context); | ||
1341 | dout(" inode %p now dirty snapc %p\n", &ci->vfs_inode, | ||
1342 | ci->i_head_snapc); | ||
1336 | BUG_ON(!list_empty(&ci->i_dirty_item)); | 1343 | BUG_ON(!list_empty(&ci->i_dirty_item)); |
1337 | spin_lock(&mdsc->cap_dirty_lock); | 1344 | spin_lock(&mdsc->cap_dirty_lock); |
1338 | list_add(&ci->i_dirty_item, &mdsc->cap_dirty); | 1345 | list_add(&ci->i_dirty_item, &mdsc->cap_dirty); |
@@ -2190,7 +2197,9 @@ void ceph_put_wrbuffer_cap_refs(struct ceph_inode_info *ci, int nr, | |||
2190 | 2197 | ||
2191 | if (ci->i_head_snapc == snapc) { | 2198 | if (ci->i_head_snapc == snapc) { |
2192 | ci->i_wrbuffer_ref_head -= nr; | 2199 | ci->i_wrbuffer_ref_head -= nr; |
2193 | if (!ci->i_wrbuffer_ref_head) { | 2200 | if (ci->i_wrbuffer_ref_head == 0 && |
2201 | ci->i_dirty_caps == 0 && ci->i_flushing_caps == 0) { | ||
2202 | BUG_ON(!ci->i_head_snapc); | ||
2194 | ceph_put_snap_context(ci->i_head_snapc); | 2203 | ceph_put_snap_context(ci->i_head_snapc); |
2195 | ci->i_head_snapc = NULL; | 2204 | ci->i_head_snapc = NULL; |
2196 | } | 2205 | } |
@@ -2483,6 +2492,11 @@ static void handle_cap_flush_ack(struct inode *inode, u64 flush_tid, | |||
2483 | dout(" inode %p now clean\n", inode); | 2492 | dout(" inode %p now clean\n", inode); |
2484 | BUG_ON(!list_empty(&ci->i_dirty_item)); | 2493 | BUG_ON(!list_empty(&ci->i_dirty_item)); |
2485 | drop = 1; | 2494 | drop = 1; |
2495 | if (ci->i_wrbuffer_ref_head == 0) { | ||
2496 | BUG_ON(!ci->i_head_snapc); | ||
2497 | ceph_put_snap_context(ci->i_head_snapc); | ||
2498 | ci->i_head_snapc = NULL; | ||
2499 | } | ||
2486 | } else { | 2500 | } else { |
2487 | BUG_ON(list_empty(&ci->i_dirty_item)); | 2501 | BUG_ON(list_empty(&ci->i_dirty_item)); |
2488 | } | 2502 | } |
diff --git a/fs/ceph/snap.c b/fs/ceph/snap.c index 6bdbf3ae7082..4868b9dcac5a 100644 --- a/fs/ceph/snap.c +++ b/fs/ceph/snap.c | |||
@@ -458,6 +458,8 @@ void ceph_queue_cap_snap(struct ceph_inode_info *ci) | |||
458 | CEPH_CAP_FILE_EXCL|CEPH_CAP_FILE_WR))) { | 458 | CEPH_CAP_FILE_EXCL|CEPH_CAP_FILE_WR))) { |
459 | struct ceph_snap_context *snapc = ci->i_head_snapc; | 459 | struct ceph_snap_context *snapc = ci->i_head_snapc; |
460 | 460 | ||
461 | dout("queue_cap_snap %p cap_snap %p queuing under %p\n", inode, | ||
462 | capsnap, snapc); | ||
461 | igrab(inode); | 463 | igrab(inode); |
462 | 464 | ||
463 | atomic_set(&capsnap->nref, 1); | 465 | atomic_set(&capsnap->nref, 1); |
@@ -489,7 +491,9 @@ void ceph_queue_cap_snap(struct ceph_inode_info *ci) | |||
489 | capsnap->dirty_pages = ci->i_wrbuffer_ref_head; | 491 | capsnap->dirty_pages = ci->i_wrbuffer_ref_head; |
490 | ci->i_wrbuffer_ref_head = 0; | 492 | ci->i_wrbuffer_ref_head = 0; |
491 | capsnap->context = snapc; | 493 | capsnap->context = snapc; |
492 | ci->i_head_snapc = NULL; | 494 | ci->i_head_snapc = |
495 | ceph_get_snap_context(ci->i_snap_realm->cached_context); | ||
496 | dout(" new snapc is %p\n", ci->i_head_snapc); | ||
493 | list_add_tail(&capsnap->ci_item, &ci->i_cap_snaps); | 497 | list_add_tail(&capsnap->ci_item, &ci->i_cap_snaps); |
494 | 498 | ||
495 | if (used & CEPH_CAP_FILE_WR) { | 499 | if (used & CEPH_CAP_FILE_WR) { |
diff --git a/fs/ceph/super.h b/fs/ceph/super.h index b33929d8f287..c33897ae5725 100644 --- a/fs/ceph/super.h +++ b/fs/ceph/super.h | |||
@@ -344,7 +344,8 @@ struct ceph_inode_info { | |||
344 | unsigned i_cap_exporting_issued; | 344 | unsigned i_cap_exporting_issued; |
345 | struct ceph_cap_reservation i_cap_migration_resv; | 345 | struct ceph_cap_reservation i_cap_migration_resv; |
346 | struct list_head i_cap_snaps; /* snapped state pending flush to mds */ | 346 | struct list_head i_cap_snaps; /* snapped state pending flush to mds */ |
347 | struct ceph_snap_context *i_head_snapc; /* set if wr_buffer_head > 0 */ | 347 | struct ceph_snap_context *i_head_snapc; /* set if wr_buffer_head > 0 or |
348 | dirty|flushing caps */ | ||
348 | unsigned i_snap_caps; /* cap bits for snapped files */ | 349 | unsigned i_snap_caps; /* cap bits for snapped files */ |
349 | 350 | ||
350 | int i_nr_by_mode[CEPH_FILE_MODE_NUM]; /* open file counts */ | 351 | int i_nr_by_mode[CEPH_FILE_MODE_NUM]; /* open file counts */ |