mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	ceph: return -EIO if read/write against filp that lost file locks
After mds evicts session, file locks get lost sliently. It's not safe to let programs continue to do read/write. Signed-off-by: "Yan, Zheng" <zyan@redhat.com> Reviewed-by: Jeff Layton <jlayton@kernel.org> Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
This commit is contained in:
		
							parent
							
								
									d468e729b7
								
							
						
					
					
						commit
						ff5d913dfc
					
				
					 3 changed files with 29 additions and 8 deletions
				
			
		| 
						 | 
					@ -2570,8 +2570,13 @@ static void __take_cap_refs(struct ceph_inode_info *ci, int got,
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * FIXME: how does a 0 return differ from -EAGAIN?
 | 
					 * FIXME: how does a 0 return differ from -EAGAIN?
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					enum {
 | 
				
			||||||
 | 
						NON_BLOCKING	= 1,
 | 
				
			||||||
 | 
						CHECK_FILELOCK	= 2,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int try_get_cap_refs(struct inode *inode, int need, int want,
 | 
					static int try_get_cap_refs(struct inode *inode, int need, int want,
 | 
				
			||||||
			    loff_t endoff, bool nonblock, int *got)
 | 
								    loff_t endoff, int flags, int *got)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct ceph_inode_info *ci = ceph_inode(inode);
 | 
						struct ceph_inode_info *ci = ceph_inode(inode);
 | 
				
			||||||
	struct ceph_mds_client *mdsc = ceph_inode_to_client(inode)->mdsc;
 | 
						struct ceph_mds_client *mdsc = ceph_inode_to_client(inode)->mdsc;
 | 
				
			||||||
| 
						 | 
					@ -2586,6 +2591,13 @@ static int try_get_cap_refs(struct inode *inode, int need, int want,
 | 
				
			||||||
again:
 | 
					again:
 | 
				
			||||||
	spin_lock(&ci->i_ceph_lock);
 | 
						spin_lock(&ci->i_ceph_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if ((flags & CHECK_FILELOCK) &&
 | 
				
			||||||
 | 
						    (ci->i_ceph_flags & CEPH_I_ERROR_FILELOCK)) {
 | 
				
			||||||
 | 
							dout("try_get_cap_refs %p error filelock\n", inode);
 | 
				
			||||||
 | 
							ret = -EIO;
 | 
				
			||||||
 | 
							goto out_unlock;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* make sure file is actually open */
 | 
						/* make sure file is actually open */
 | 
				
			||||||
	file_wanted = __ceph_caps_file_wanted(ci);
 | 
						file_wanted = __ceph_caps_file_wanted(ci);
 | 
				
			||||||
	if ((file_wanted & need) != need) {
 | 
						if ((file_wanted & need) != need) {
 | 
				
			||||||
| 
						 | 
					@ -2647,7 +2659,7 @@ static int try_get_cap_refs(struct inode *inode, int need, int want,
 | 
				
			||||||
					 * we can not call down_read() when
 | 
										 * we can not call down_read() when
 | 
				
			||||||
					 * task isn't in TASK_RUNNING state
 | 
										 * task isn't in TASK_RUNNING state
 | 
				
			||||||
					 */
 | 
										 */
 | 
				
			||||||
					if (nonblock) {
 | 
										if (flags & NON_BLOCKING) {
 | 
				
			||||||
						ret = -EAGAIN;
 | 
											ret = -EAGAIN;
 | 
				
			||||||
						goto out_unlock;
 | 
											goto out_unlock;
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
| 
						 | 
					@ -2752,7 +2764,8 @@ int ceph_try_get_caps(struct inode *inode, int need, int want,
 | 
				
			||||||
	if (ret < 0)
 | 
						if (ret < 0)
 | 
				
			||||||
		return ret;
 | 
							return ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ret = try_get_cap_refs(inode, need, want, 0, nonblock, got);
 | 
						ret = try_get_cap_refs(inode, need, want, 0,
 | 
				
			||||||
 | 
								       (nonblock ? NON_BLOCKING : 0), got);
 | 
				
			||||||
	return ret == -EAGAIN ? 0 : ret;
 | 
						return ret == -EAGAIN ? 0 : ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2764,9 +2777,10 @@ int ceph_try_get_caps(struct inode *inode, int need, int want,
 | 
				
			||||||
int ceph_get_caps(struct file *filp, int need, int want,
 | 
					int ceph_get_caps(struct file *filp, int need, int want,
 | 
				
			||||||
		  loff_t endoff, int *got, struct page **pinned_page)
 | 
							  loff_t endoff, int *got, struct page **pinned_page)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						struct ceph_file_info *fi = filp->private_data;
 | 
				
			||||||
	struct inode *inode = file_inode(filp);
 | 
						struct inode *inode = file_inode(filp);
 | 
				
			||||||
	struct ceph_inode_info *ci = ceph_inode(inode);
 | 
						struct ceph_inode_info *ci = ceph_inode(inode);
 | 
				
			||||||
	int _got, ret;
 | 
						int ret, _got, flags;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ret = ceph_pool_perm_check(inode, need);
 | 
						ret = ceph_pool_perm_check(inode, need);
 | 
				
			||||||
	if (ret < 0)
 | 
						if (ret < 0)
 | 
				
			||||||
| 
						 | 
					@ -2776,17 +2790,19 @@ int ceph_get_caps(struct file *filp, int need, int want,
 | 
				
			||||||
		if (endoff > 0)
 | 
							if (endoff > 0)
 | 
				
			||||||
			check_max_size(inode, endoff);
 | 
								check_max_size(inode, endoff);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							flags = atomic_read(&fi->num_locks) ? CHECK_FILELOCK : 0;
 | 
				
			||||||
		_got = 0;
 | 
							_got = 0;
 | 
				
			||||||
		ret = try_get_cap_refs(inode, need, want, endoff,
 | 
							ret = try_get_cap_refs(inode, need, want, endoff,
 | 
				
			||||||
				       false, &_got);
 | 
									       flags, &_got);
 | 
				
			||||||
		if (ret == -EAGAIN)
 | 
							if (ret == -EAGAIN)
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
		if (!ret) {
 | 
							if (!ret) {
 | 
				
			||||||
			DEFINE_WAIT_FUNC(wait, woken_wake_function);
 | 
								DEFINE_WAIT_FUNC(wait, woken_wake_function);
 | 
				
			||||||
			add_wait_queue(&ci->i_cap_wq, &wait);
 | 
								add_wait_queue(&ci->i_cap_wq, &wait);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								flags |= NON_BLOCKING;
 | 
				
			||||||
			while (!(ret = try_get_cap_refs(inode, need, want,
 | 
								while (!(ret = try_get_cap_refs(inode, need, want,
 | 
				
			||||||
							endoff, true, &_got))) {
 | 
												endoff, flags, &_got))) {
 | 
				
			||||||
				if (signal_pending(current)) {
 | 
									if (signal_pending(current)) {
 | 
				
			||||||
					ret = -ERESTARTSYS;
 | 
										ret = -ERESTARTSYS;
 | 
				
			||||||
					break;
 | 
										break;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -32,14 +32,18 @@ void __init ceph_flock_init(void)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void ceph_fl_copy_lock(struct file_lock *dst, struct file_lock *src)
 | 
					static void ceph_fl_copy_lock(struct file_lock *dst, struct file_lock *src)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct inode *inode = file_inode(src->fl_file);
 | 
						struct ceph_file_info *fi = dst->fl_file->private_data;
 | 
				
			||||||
 | 
						struct inode *inode = file_inode(dst->fl_file);
 | 
				
			||||||
	atomic_inc(&ceph_inode(inode)->i_filelock_ref);
 | 
						atomic_inc(&ceph_inode(inode)->i_filelock_ref);
 | 
				
			||||||
 | 
						atomic_inc(&fi->num_locks);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void ceph_fl_release_lock(struct file_lock *fl)
 | 
					static void ceph_fl_release_lock(struct file_lock *fl)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						struct ceph_file_info *fi = fl->fl_file->private_data;
 | 
				
			||||||
	struct inode *inode = file_inode(fl->fl_file);
 | 
						struct inode *inode = file_inode(fl->fl_file);
 | 
				
			||||||
	struct ceph_inode_info *ci = ceph_inode(inode);
 | 
						struct ceph_inode_info *ci = ceph_inode(inode);
 | 
				
			||||||
 | 
						atomic_dec(&fi->num_locks);
 | 
				
			||||||
	if (atomic_dec_and_test(&ci->i_filelock_ref)) {
 | 
						if (atomic_dec_and_test(&ci->i_filelock_ref)) {
 | 
				
			||||||
		/* clear error when all locks are released */
 | 
							/* clear error when all locks are released */
 | 
				
			||||||
		spin_lock(&ci->i_ceph_lock);
 | 
							spin_lock(&ci->i_ceph_lock);
 | 
				
			||||||
| 
						 | 
					@ -73,7 +77,7 @@ static int ceph_lock_message(u8 lock_type, u16 operation, struct inode *inode,
 | 
				
			||||||
		 * window. Caller function will decrease the counter.
 | 
							 * window. Caller function will decrease the counter.
 | 
				
			||||||
		 */
 | 
							 */
 | 
				
			||||||
		fl->fl_ops = &ceph_fl_lock_ops;
 | 
							fl->fl_ops = &ceph_fl_lock_ops;
 | 
				
			||||||
		atomic_inc(&ceph_inode(inode)->i_filelock_ref);
 | 
							fl->fl_ops->fl_copy_lock(fl, NULL);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (operation != CEPH_MDS_OP_SETFILELOCK || cmd == CEPH_LOCK_UNLOCK)
 | 
						if (operation != CEPH_MDS_OP_SETFILELOCK || cmd == CEPH_LOCK_UNLOCK)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -707,6 +707,7 @@ struct ceph_file_info {
 | 
				
			||||||
	struct list_head rw_contexts;
 | 
						struct list_head rw_contexts;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	errseq_t meta_err;
 | 
						errseq_t meta_err;
 | 
				
			||||||
 | 
						atomic_t num_locks;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct ceph_dir_file_info {
 | 
					struct ceph_dir_file_info {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue