ceph: return -EIO if read/write against filp that lost file locks
authorYan, Zheng <zyan@redhat.com>
Thu, 25 Jul 2019 12:16:45 +0000 (20:16 +0800)
committerIlya Dryomov <idryomov@gmail.com>
Mon, 16 Sep 2019 10:06:24 +0000 (12:06 +0200)
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>
fs/ceph/caps.c
fs/ceph/locks.c
fs/ceph/super.h

index bde81aa..102192c 100644 (file)
@@ -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?
  */
+enum {
+       NON_BLOCKING    = 1,
+       CHECK_FILELOCK  = 2,
+};
+
 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_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:
        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 */
        file_wanted = __ceph_caps_file_wanted(ci);
        if ((file_wanted & need) != need) {
@@ -2647,7 +2659,7 @@ again:
                                         * we can not call down_read() when
                                         * task isn't in TASK_RUNNING state
                                         */
-                                       if (nonblock) {
+                                       if (flags & NON_BLOCKING) {
                                                ret = -EAGAIN;
                                                goto out_unlock;
                                        }
@@ -2752,7 +2764,8 @@ int ceph_try_get_caps(struct inode *inode, int need, int want,
        if (ret < 0)
                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;
 }
 
@@ -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,
                  loff_t endoff, int *got, struct page **pinned_page)
 {
+       struct ceph_file_info *fi = filp->private_data;
        struct inode *inode = file_inode(filp);
        struct ceph_inode_info *ci = ceph_inode(inode);
-       int _got, ret;
+       int ret, _got, flags;
 
        ret = ceph_pool_perm_check(inode, need);
        if (ret < 0)
@@ -2776,17 +2790,19 @@ int ceph_get_caps(struct file *filp, int need, int want,
                if (endoff > 0)
                        check_max_size(inode, endoff);
 
+               flags = atomic_read(&fi->num_locks) ? CHECK_FILELOCK : 0;
                _got = 0;
                ret = try_get_cap_refs(inode, need, want, endoff,
-                                      false, &_got);
+                                      flags, &_got);
                if (ret == -EAGAIN)
                        continue;
                if (!ret) {
                        DEFINE_WAIT_FUNC(wait, woken_wake_function);
                        add_wait_queue(&ci->i_cap_wq, &wait);
 
+                       flags |= NON_BLOCKING;
                        while (!(ret = try_get_cap_refs(inode, need, want,
-                                                       endoff, true, &_got))) {
+                                                       endoff, flags, &_got))) {
                                if (signal_pending(current)) {
                                        ret = -ERESTARTSYS;
                                        break;
index 5083e23..544e9e8 100644 (file)
@@ -32,14 +32,18 @@ void __init ceph_flock_init(void)
 
 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(&fi->num_locks);
 }
 
 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 ceph_inode_info *ci = ceph_inode(inode);
+       atomic_dec(&fi->num_locks);
        if (atomic_dec_and_test(&ci->i_filelock_ref)) {
                /* clear error when all locks are released */
                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.
                 */
                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)
index 7af7f2b..7363182 100644 (file)
@@ -707,6 +707,7 @@ struct ceph_file_info {
        struct list_head rw_contexts;
 
        errseq_t meta_err;
+       atomic_t num_locks;
 };
 
 struct ceph_dir_file_info {