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>
This commit is contained in:
		
							parent
							
								
									4d1829a59d
								
							
						
					
					
						commit
						6f60f88947
					
				| @ -61,6 +61,14 @@ struct inode *ceph_get_inode(struct super_block *sb, struct ceph_vino vino) | ||||
| 	return inode; | ||||
| } | ||||
| 
 | ||||
| struct inode *ceph_lookup_inode(struct super_block *sb, struct ceph_vino vino) | ||||
| { | ||||
| 	struct inode *inode; | ||||
| 	ino_t t = ceph_vino_to_ino(vino); | ||||
| 	inode = ilookup5_nowait(sb, t, ceph_ino_compare, &vino); | ||||
| 	return inode; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * get/constuct snapdir inode for a given directory | ||||
|  */ | ||||
|  | ||||
| @ -1031,6 +1031,37 @@ static void remove_session_caps(struct ceph_mds_session *session) | ||||
| { | ||||
| 	dout("remove_session_caps on %p\n", session); | ||||
| 	iterate_session_caps(session, remove_session_caps_cb, NULL); | ||||
| 
 | ||||
| 	spin_lock(&session->s_cap_lock); | ||||
| 	if (session->s_nr_caps > 0) { | ||||
| 		struct super_block *sb = session->s_mdsc->fsc->sb; | ||||
| 		struct inode *inode; | ||||
| 		struct ceph_cap *cap, *prev = NULL; | ||||
| 		struct ceph_vino vino; | ||||
| 		/*
 | ||||
| 		 * iterate_session_caps() skips inodes that are being | ||||
| 		 * deleted, we need to wait until deletions are complete. | ||||
| 		 * __wait_on_freeing_inode() is designed for the job, | ||||
| 		 * but it is not exported, so use lookup inode function | ||||
| 		 * to access it. | ||||
| 		 */ | ||||
| 		while (!list_empty(&session->s_caps)) { | ||||
| 			cap = list_entry(session->s_caps.next, | ||||
| 					 struct ceph_cap, session_caps); | ||||
| 			if (cap == prev) | ||||
| 				break; | ||||
| 			prev = cap; | ||||
| 			vino = cap->ci->i_vino; | ||||
| 			spin_unlock(&session->s_cap_lock); | ||||
| 
 | ||||
| 			inode = ceph_lookup_inode(sb, vino); | ||||
| 			iput(inode); | ||||
| 
 | ||||
| 			spin_lock(&session->s_cap_lock); | ||||
| 		} | ||||
| 	} | ||||
| 	spin_unlock(&session->s_cap_lock); | ||||
| 
 | ||||
| 	BUG_ON(session->s_nr_caps > 0); | ||||
| 	BUG_ON(!list_empty(&session->s_cap_flushing)); | ||||
| 	cleanup_cap_releases(session); | ||||
|  | ||||
| @ -677,6 +677,8 @@ extern void ceph_destroy_inode(struct inode *inode); | ||||
| 
 | ||||
| extern struct inode *ceph_get_inode(struct super_block *sb, | ||||
| 				    struct ceph_vino vino); | ||||
| extern struct inode *ceph_lookup_inode(struct super_block *sb, | ||||
| 				       struct ceph_vino vino); | ||||
| extern struct inode *ceph_get_snapdir(struct inode *parent); | ||||
| extern int ceph_fill_file_size(struct inode *inode, int issued, | ||||
| 			       u32 truncate_seq, u64 truncate_size, u64 size); | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user