diff options
author | Yan, Zheng <zheng.z.yan@intel.com> | 2013-07-24 00:22:11 -0400 |
---|---|---|
committer | Sage Weil <sage@inktank.com> | 2013-08-09 20:55:32 -0400 |
commit | 6f60f889470aecf747610279545c054a99aadca3 (patch) | |
tree | 60f5781c7673d1d0e06ef1b658e652b2738daf24 /fs/ceph | |
parent | 4d1829a59de402fc95daf4576c51aa0a7439aee8 (diff) |
ceph: fix freeing inode vs removing session caps race
remove_session_caps() uses iterate_session_caps() to remove caps,
but iterate_session_caps() skips inodes that are being deleted.
So session->s_nr_caps can be non-zero after iterate_session_caps()
return.
We can fix the issue by waiting until deletions are complete.
__wait_on_freeing_inode() is designed for the job, but it is not
exported, so we use lookup inode function to access it.
Signed-off-by: Yan, Zheng <zheng.z.yan@intel.com>
Diffstat (limited to 'fs/ceph')
-rw-r--r-- | fs/ceph/inode.c | 8 | ||||
-rw-r--r-- | fs/ceph/mds_client.c | 31 | ||||
-rw-r--r-- | fs/ceph/super.h | 2 |
3 files changed, 41 insertions, 0 deletions
diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c index 55aaddb4047e..3b0abed667c2 100644 --- a/fs/ceph/inode.c +++ b/fs/ceph/inode.c | |||
@@ -61,6 +61,14 @@ struct inode *ceph_get_inode(struct super_block *sb, struct ceph_vino vino) | |||
61 | return inode; | 61 | return inode; |
62 | } | 62 | } |
63 | 63 | ||
64 | struct inode *ceph_lookup_inode(struct super_block *sb, struct ceph_vino vino) | ||
65 | { | ||
66 | struct inode *inode; | ||
67 | ino_t t = ceph_vino_to_ino(vino); | ||
68 | inode = ilookup5_nowait(sb, t, ceph_ino_compare, &vino); | ||
69 | return inode; | ||
70 | } | ||
71 | |||
64 | /* | 72 | /* |
65 | * get/constuct snapdir inode for a given directory | 73 | * get/constuct snapdir inode for a given directory |
66 | */ | 74 | */ |
diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 6b40d8112c64..cbf08203e00d 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c | |||
@@ -1031,6 +1031,37 @@ static void remove_session_caps(struct ceph_mds_session *session) | |||
1031 | { | 1031 | { |
1032 | dout("remove_session_caps on %p\n", session); | 1032 | dout("remove_session_caps on %p\n", session); |
1033 | iterate_session_caps(session, remove_session_caps_cb, NULL); | 1033 | iterate_session_caps(session, remove_session_caps_cb, NULL); |
1034 | |||
1035 | spin_lock(&session->s_cap_lock); | ||
1036 | if (session->s_nr_caps > 0) { | ||
1037 | struct super_block *sb = session->s_mdsc->fsc->sb; | ||
1038 | struct inode *inode; | ||
1039 | struct ceph_cap *cap, *prev = NULL; | ||
1040 | struct ceph_vino vino; | ||
1041 | /* | ||
1042 | * iterate_session_caps() skips inodes that are being | ||
1043 | * deleted, we need to wait until deletions are complete. | ||
1044 | * __wait_on_freeing_inode() is designed for the job, | ||
1045 | * but it is not exported, so use lookup inode function | ||
1046 | * to access it. | ||
1047 | */ | ||
1048 | while (!list_empty(&session->s_caps)) { | ||
1049 | cap = list_entry(session->s_caps.next, | ||
1050 | struct ceph_cap, session_caps); | ||
1051 | if (cap == prev) | ||
1052 | break; | ||
1053 | prev = cap; | ||
1054 | vino = cap->ci->i_vino; | ||
1055 | spin_unlock(&session->s_cap_lock); | ||
1056 | |||
1057 | inode = ceph_lookup_inode(sb, vino); | ||
1058 | iput(inode); | ||
1059 | |||
1060 | spin_lock(&session->s_cap_lock); | ||
1061 | } | ||
1062 | } | ||
1063 | spin_unlock(&session->s_cap_lock); | ||
1064 | |||
1034 | BUG_ON(session->s_nr_caps > 0); | 1065 | BUG_ON(session->s_nr_caps > 0); |
1035 | BUG_ON(!list_empty(&session->s_cap_flushing)); | 1066 | BUG_ON(!list_empty(&session->s_cap_flushing)); |
1036 | cleanup_cap_releases(session); | 1067 | cleanup_cap_releases(session); |
diff --git a/fs/ceph/super.h b/fs/ceph/super.h index cbded572345e..afcd62a68916 100644 --- a/fs/ceph/super.h +++ b/fs/ceph/super.h | |||
@@ -677,6 +677,8 @@ extern void ceph_destroy_inode(struct inode *inode); | |||
677 | 677 | ||
678 | extern struct inode *ceph_get_inode(struct super_block *sb, | 678 | extern struct inode *ceph_get_inode(struct super_block *sb, |
679 | struct ceph_vino vino); | 679 | struct ceph_vino vino); |
680 | extern struct inode *ceph_lookup_inode(struct super_block *sb, | ||
681 | struct ceph_vino vino); | ||
680 | extern struct inode *ceph_get_snapdir(struct inode *parent); | 682 | extern struct inode *ceph_get_snapdir(struct inode *parent); |
681 | extern int ceph_fill_file_size(struct inode *inode, int issued, | 683 | extern int ceph_fill_file_size(struct inode *inode, int issued, |
682 | u32 truncate_seq, u64 truncate_size, u64 size); | 684 | u32 truncate_seq, u64 truncate_size, u64 size); |