Commit ff5d913d authored by Yan, Zheng's avatar Yan, Zheng Committed by Ilya Dryomov

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: default avatar"Yan, Zheng" <zyan@redhat.com>
Reviewed-by: default avatarJeff Layton <jlayton@kernel.org>
Signed-off-by: default avatarIlya Dryomov <idryomov@gmail.com>
parent d468e729
...@@ -2570,8 +2570,13 @@ static void __take_cap_refs(struct ceph_inode_info *ci, int got, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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) ...@@ -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, ...@@ -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 { ...@@ -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 {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment