diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2013-11-26 21:02:46 -0500 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-11-26 21:02:46 -0500 |
| commit | 4f9e5df211a8591b3fb5c154ecc7ec5d4ecd6b79 (patch) | |
| tree | ba4e3980540d5bf082bc38f0107b628b2b726a25 | |
| parent | 0e4b0743bbe5807535ba1b0389281f9a4c1b2bb7 (diff) | |
| parent | ff638b7df5a9264024a6448bdfde2b2bf5d1994a (diff) | |
Merge branch 'for-linus-bugs' of git://git.kernel.org/pub/scm/linux/kernel/git/sage/ceph-client
Pull ceph bug-fixes from Sage Weil:
"These include a couple fixes to the new fscache code that went in
during the last cycle (which will need to go stable@ shortly as well),
a couple client-side directory fragmentation fixes, a fix for a race
in the cap release queuing path, and a couple race fixes in the
request abort and resend code.
Obviously some of this could have gone into 3.12 final, but I
preferred to overtest rather than send things in for a late -rc, and
then my travel schedule intervened"
* 'for-linus-bugs' of git://git.kernel.org/pub/scm/linux/kernel/git/sage/ceph-client:
ceph: allocate non-zero page to fscache in readpage()
ceph: wake up 'safe' waiters when unregistering request
ceph: cleanup aborted requests when re-sending requests.
ceph: handle race between cap reconnect and cap release
ceph: set caps count after composing cap reconnect message
ceph: queue cap release in __ceph_remove_cap()
ceph: handle frag mismatch between readdir request and reply
ceph: remove outdated frag information
ceph: hung on ceph fscache invalidate in some cases
| -rw-r--r-- | fs/ceph/addr.c | 2 | ||||
| -rw-r--r-- | fs/ceph/cache.c | 3 | ||||
| -rw-r--r-- | fs/ceph/caps.c | 27 | ||||
| -rw-r--r-- | fs/ceph/dir.c | 11 | ||||
| -rw-r--r-- | fs/ceph/inode.c | 49 | ||||
| -rw-r--r-- | fs/ceph/mds_client.c | 61 | ||||
| -rw-r--r-- | fs/ceph/mds_client.h | 1 | ||||
| -rw-r--r-- | fs/ceph/super.h | 8 |
8 files changed, 121 insertions, 41 deletions
diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c index 6df8bd481425..1e561c059539 100644 --- a/fs/ceph/addr.c +++ b/fs/ceph/addr.c | |||
| @@ -216,7 +216,7 @@ static int readpage_nounlock(struct file *filp, struct page *page) | |||
| 216 | } | 216 | } |
| 217 | SetPageUptodate(page); | 217 | SetPageUptodate(page); |
| 218 | 218 | ||
| 219 | if (err == 0) | 219 | if (err >= 0) |
| 220 | ceph_readpage_to_fscache(inode, page); | 220 | ceph_readpage_to_fscache(inode, page); |
| 221 | 221 | ||
| 222 | out: | 222 | out: |
diff --git a/fs/ceph/cache.c b/fs/ceph/cache.c index 7db2e6ca4b8f..8c44fdd4e1c3 100644 --- a/fs/ceph/cache.c +++ b/fs/ceph/cache.c | |||
| @@ -324,6 +324,9 @@ void ceph_invalidate_fscache_page(struct inode* inode, struct page *page) | |||
| 324 | { | 324 | { |
| 325 | struct ceph_inode_info *ci = ceph_inode(inode); | 325 | struct ceph_inode_info *ci = ceph_inode(inode); |
| 326 | 326 | ||
| 327 | if (!PageFsCache(page)) | ||
| 328 | return; | ||
| 329 | |||
| 327 | fscache_wait_on_page_write(ci->fscache, page); | 330 | fscache_wait_on_page_write(ci->fscache, page); |
| 328 | fscache_uncache_page(ci->fscache, page); | 331 | fscache_uncache_page(ci->fscache, page); |
| 329 | } | 332 | } |
diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index 13976c33332e..3c0a4bd74996 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c | |||
| @@ -897,7 +897,7 @@ static int __ceph_is_any_caps(struct ceph_inode_info *ci) | |||
| 897 | * caller should hold i_ceph_lock. | 897 | * caller should hold i_ceph_lock. |
| 898 | * caller will not hold session s_mutex if called from destroy_inode. | 898 | * caller will not hold session s_mutex if called from destroy_inode. |
| 899 | */ | 899 | */ |
| 900 | void __ceph_remove_cap(struct ceph_cap *cap) | 900 | void __ceph_remove_cap(struct ceph_cap *cap, bool queue_release) |
| 901 | { | 901 | { |
| 902 | struct ceph_mds_session *session = cap->session; | 902 | struct ceph_mds_session *session = cap->session; |
| 903 | struct ceph_inode_info *ci = cap->ci; | 903 | struct ceph_inode_info *ci = cap->ci; |
| @@ -909,6 +909,16 @@ void __ceph_remove_cap(struct ceph_cap *cap) | |||
| 909 | 909 | ||
| 910 | /* remove from session list */ | 910 | /* remove from session list */ |
| 911 | spin_lock(&session->s_cap_lock); | 911 | spin_lock(&session->s_cap_lock); |
| 912 | /* | ||
| 913 | * s_cap_reconnect is protected by s_cap_lock. no one changes | ||
| 914 | * s_cap_gen while session is in the reconnect state. | ||
| 915 | */ | ||
| 916 | if (queue_release && | ||
| 917 | (!session->s_cap_reconnect || | ||
| 918 | cap->cap_gen == session->s_cap_gen)) | ||
| 919 | __queue_cap_release(session, ci->i_vino.ino, cap->cap_id, | ||
| 920 | cap->mseq, cap->issue_seq); | ||
| 921 | |||
| 912 | if (session->s_cap_iterator == cap) { | 922 | if (session->s_cap_iterator == cap) { |
| 913 | /* not yet, we are iterating over this very cap */ | 923 | /* not yet, we are iterating over this very cap */ |
| 914 | dout("__ceph_remove_cap delaying %p removal from session %p\n", | 924 | dout("__ceph_remove_cap delaying %p removal from session %p\n", |
| @@ -1023,7 +1033,6 @@ void __queue_cap_release(struct ceph_mds_session *session, | |||
| 1023 | struct ceph_mds_cap_release *head; | 1033 | struct ceph_mds_cap_release *head; |
| 1024 | struct ceph_mds_cap_item *item; | 1034 | struct ceph_mds_cap_item *item; |
| 1025 | 1035 | ||
| 1026 | spin_lock(&session->s_cap_lock); | ||
| 1027 | BUG_ON(!session->s_num_cap_releases); | 1036 | BUG_ON(!session->s_num_cap_releases); |
| 1028 | msg = list_first_entry(&session->s_cap_releases, | 1037 | msg = list_first_entry(&session->s_cap_releases, |
| 1029 | struct ceph_msg, list_head); | 1038 | struct ceph_msg, list_head); |
| @@ -1052,7 +1061,6 @@ void __queue_cap_release(struct ceph_mds_session *session, | |||
| 1052 | (int)CEPH_CAPS_PER_RELEASE, | 1061 | (int)CEPH_CAPS_PER_RELEASE, |
| 1053 | (int)msg->front.iov_len); | 1062 | (int)msg->front.iov_len); |
| 1054 | } | 1063 | } |
| 1055 | spin_unlock(&session->s_cap_lock); | ||
| 1056 | } | 1064 | } |
| 1057 | 1065 | ||
| 1058 | /* | 1066 | /* |
| @@ -1067,12 +1075,8 @@ void ceph_queue_caps_release(struct inode *inode) | |||
| 1067 | p = rb_first(&ci->i_caps); | 1075 | p = rb_first(&ci->i_caps); |
| 1068 | while (p) { | 1076 | while (p) { |
| 1069 | struct ceph_cap *cap = rb_entry(p, struct ceph_cap, ci_node); | 1077 | struct ceph_cap *cap = rb_entry(p, struct ceph_cap, ci_node); |
| 1070 | struct ceph_mds_session *session = cap->session; | ||
| 1071 | |||
| 1072 | __queue_cap_release(session, ceph_ino(inode), cap->cap_id, | ||
| 1073 | cap->mseq, cap->issue_seq); | ||
| 1074 | p = rb_next(p); | 1078 | p = rb_next(p); |
| 1075 | __ceph_remove_cap(cap); | 1079 | __ceph_remove_cap(cap, true); |
| 1076 | } | 1080 | } |
| 1077 | } | 1081 | } |
| 1078 | 1082 | ||
| @@ -2791,7 +2795,7 @@ static void handle_cap_export(struct inode *inode, struct ceph_mds_caps *ex, | |||
| 2791 | } | 2795 | } |
| 2792 | spin_unlock(&mdsc->cap_dirty_lock); | 2796 | spin_unlock(&mdsc->cap_dirty_lock); |
| 2793 | } | 2797 | } |
| 2794 | __ceph_remove_cap(cap); | 2798 | __ceph_remove_cap(cap, false); |
| 2795 | } | 2799 | } |
| 2796 | /* else, we already released it */ | 2800 | /* else, we already released it */ |
| 2797 | 2801 | ||
| @@ -2931,9 +2935,12 @@ void ceph_handle_caps(struct ceph_mds_session *session, | |||
| 2931 | if (!inode) { | 2935 | if (!inode) { |
| 2932 | dout(" i don't have ino %llx\n", vino.ino); | 2936 | dout(" i don't have ino %llx\n", vino.ino); |
| 2933 | 2937 | ||
| 2934 | if (op == CEPH_CAP_OP_IMPORT) | 2938 | if (op == CEPH_CAP_OP_IMPORT) { |
| 2939 | spin_lock(&session->s_cap_lock); | ||
| 2935 | __queue_cap_release(session, vino.ino, cap_id, | 2940 | __queue_cap_release(session, vino.ino, cap_id, |
| 2936 | mseq, seq); | 2941 | mseq, seq); |
| 2942 | spin_unlock(&session->s_cap_lock); | ||
| 2943 | } | ||
| 2937 | goto flush_cap_releases; | 2944 | goto flush_cap_releases; |
| 2938 | } | 2945 | } |
| 2939 | 2946 | ||
diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c index 868b61d56cac..2a0bcaeb189a 100644 --- a/fs/ceph/dir.c +++ b/fs/ceph/dir.c | |||
| @@ -352,8 +352,18 @@ more: | |||
| 352 | } | 352 | } |
| 353 | 353 | ||
| 354 | /* note next offset and last dentry name */ | 354 | /* note next offset and last dentry name */ |
| 355 | rinfo = &req->r_reply_info; | ||
| 356 | if (le32_to_cpu(rinfo->dir_dir->frag) != frag) { | ||
| 357 | frag = le32_to_cpu(rinfo->dir_dir->frag); | ||
| 358 | if (ceph_frag_is_leftmost(frag)) | ||
| 359 | fi->next_offset = 2; | ||
| 360 | else | ||
| 361 | fi->next_offset = 0; | ||
| 362 | off = fi->next_offset; | ||
| 363 | } | ||
| 355 | fi->offset = fi->next_offset; | 364 | fi->offset = fi->next_offset; |
| 356 | fi->last_readdir = req; | 365 | fi->last_readdir = req; |
| 366 | fi->frag = frag; | ||
| 357 | 367 | ||
| 358 | if (req->r_reply_info.dir_end) { | 368 | if (req->r_reply_info.dir_end) { |
| 359 | kfree(fi->last_name); | 369 | kfree(fi->last_name); |
| @@ -363,7 +373,6 @@ more: | |||
| 363 | else | 373 | else |
| 364 | fi->next_offset = 0; | 374 | fi->next_offset = 0; |
| 365 | } else { | 375 | } else { |
| 366 | rinfo = &req->r_reply_info; | ||
| 367 | err = note_last_dentry(fi, | 376 | err = note_last_dentry(fi, |
| 368 | rinfo->dir_dname[rinfo->dir_nr-1], | 377 | rinfo->dir_dname[rinfo->dir_nr-1], |
| 369 | rinfo->dir_dname_len[rinfo->dir_nr-1]); | 378 | rinfo->dir_dname_len[rinfo->dir_nr-1]); |
diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c index 8549a48115f7..9a8e396aed89 100644 --- a/fs/ceph/inode.c +++ b/fs/ceph/inode.c | |||
| @@ -577,6 +577,8 @@ static int fill_inode(struct inode *inode, | |||
| 577 | int issued = 0, implemented; | 577 | int issued = 0, implemented; |
| 578 | struct timespec mtime, atime, ctime; | 578 | struct timespec mtime, atime, ctime; |
| 579 | u32 nsplits; | 579 | u32 nsplits; |
| 580 | struct ceph_inode_frag *frag; | ||
| 581 | struct rb_node *rb_node; | ||
| 580 | struct ceph_buffer *xattr_blob = NULL; | 582 | struct ceph_buffer *xattr_blob = NULL; |
| 581 | int err = 0; | 583 | int err = 0; |
| 582 | int queue_trunc = 0; | 584 | int queue_trunc = 0; |
| @@ -751,15 +753,38 @@ no_change: | |||
| 751 | /* FIXME: move me up, if/when version reflects fragtree changes */ | 753 | /* FIXME: move me up, if/when version reflects fragtree changes */ |
| 752 | nsplits = le32_to_cpu(info->fragtree.nsplits); | 754 | nsplits = le32_to_cpu(info->fragtree.nsplits); |
| 753 | mutex_lock(&ci->i_fragtree_mutex); | 755 | mutex_lock(&ci->i_fragtree_mutex); |
| 756 | rb_node = rb_first(&ci->i_fragtree); | ||
| 754 | for (i = 0; i < nsplits; i++) { | 757 | for (i = 0; i < nsplits; i++) { |
| 755 | u32 id = le32_to_cpu(info->fragtree.splits[i].frag); | 758 | u32 id = le32_to_cpu(info->fragtree.splits[i].frag); |
| 756 | struct ceph_inode_frag *frag = __get_or_create_frag(ci, id); | 759 | frag = NULL; |
| 757 | 760 | while (rb_node) { | |
| 758 | if (IS_ERR(frag)) | 761 | frag = rb_entry(rb_node, struct ceph_inode_frag, node); |
| 759 | continue; | 762 | if (ceph_frag_compare(frag->frag, id) >= 0) { |
| 763 | if (frag->frag != id) | ||
| 764 | frag = NULL; | ||
| 765 | else | ||
| 766 | rb_node = rb_next(rb_node); | ||
| 767 | break; | ||
| 768 | } | ||
| 769 | rb_node = rb_next(rb_node); | ||
