ceph: consider inode's last read/write when calculating wanted caps
authorYan, Zheng <zyan@redhat.com>
Thu, 5 Mar 2020 12:21:00 +0000 (20:21 +0800)
committerIlya Dryomov <idryomov@gmail.com>
Mon, 30 Mar 2020 10:42:42 +0000 (12:42 +0200)
Add i_last_rd and i_last_wr to ceph_inode_info. These fields are
used to track the last time the client acquired read/write caps for
the inode.

If there is no read/write on an inode for 'caps_wanted_delay_max'
seconds, __ceph_caps_file_wanted() does not request caps for read/write
even there are open files.

Call __ceph_touch_fmode() for dir operations. __ceph_caps_file_wanted()
calculates dir's wanted caps according to last dir read/modification. If
there is recent dir read, dir inode wants CEPH_CAP_ANY_SHARED caps. If
there is recent dir modification, also wants CEPH_CAP_FILE_EXCL.

Readdir is a special case. Dir inode wants CEPH_CAP_FILE_EXCL after
readdir, as with that, modifications do not need to release
CEPH_CAP_FILE_SHARED or invalidate all dentry leases issued by readdir.

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/dir.c
fs/ceph/file.c
fs/ceph/inode.c
fs/ceph/ioctl.c
fs/ceph/mds_client.c
fs/ceph/super.h
include/linux/ceph/ceph_fs.h

index a75e5eb..aa5bb5a 100644 (file)
@@ -978,19 +978,67 @@ int __ceph_caps_used(struct ceph_inode_info *ci)
        return used;
 }
 
+#define FMODE_WAIT_BIAS 1000
+
 /*
  * wanted, by virtue of open file modes
  */
 int __ceph_caps_file_wanted(struct ceph_inode_info *ci)
 {
-       int i, bits = 0;
-       for (i = 0; i < CEPH_FILE_MODE_BITS; i++) {
-               if (ci->i_nr_by_mode[i])
-                       bits |= 1 << i;
+       const int PIN_SHIFT = ffs(CEPH_FILE_MODE_PIN);
+       const int RD_SHIFT = ffs(CEPH_FILE_MODE_RD);
+       const int WR_SHIFT = ffs(CEPH_FILE_MODE_WR);
+       const int LAZY_SHIFT = ffs(CEPH_FILE_MODE_LAZY);
+       struct ceph_mount_options *opt =
+               ceph_inode_to_client(&ci->vfs_inode)->mount_options;
+       unsigned long used_cutoff = jiffies - opt->caps_wanted_delay_max * HZ;
+       unsigned long idle_cutoff = jiffies - opt->caps_wanted_delay_min * HZ;
+
+       if (S_ISDIR(ci->vfs_inode.i_mode)) {
+               int want = 0;
+
+               /* use used_cutoff here, to keep dir's wanted caps longer */
+               if (ci->i_nr_by_mode[RD_SHIFT] > 0 ||
+                   time_after(ci->i_last_rd, used_cutoff))
+                       want |= CEPH_CAP_ANY_SHARED;
+
+               if (ci->i_nr_by_mode[WR_SHIFT] > 0 ||
+                   time_after(ci->i_last_wr, used_cutoff)) {
+                       want |= CEPH_CAP_ANY_SHARED | CEPH_CAP_FILE_EXCL;
+                       if (opt->flags & CEPH_MOUNT_OPT_ASYNC_DIROPS)
+                               want |= CEPH_CAP_ANY_DIR_OPS;
+               }
+
+               if (want || ci->i_nr_by_mode[PIN_SHIFT] > 0)
+                       want |= CEPH_CAP_PIN;
+
+               return want;
+       } else {
+               int bits = 0;
+
+               if (ci->i_nr_by_mode[RD_SHIFT] > 0) {
+                       if (ci->i_nr_by_mode[RD_SHIFT] >= FMODE_WAIT_BIAS ||
+                           time_after(ci->i_last_rd, used_cutoff))
+                               bits |= 1 << RD_SHIFT;
+               } else if (time_after(ci->i_last_rd, idle_cutoff)) {
+                       bits |= 1 << RD_SHIFT;
+               }
+
+               if (ci->i_nr_by_mode[WR_SHIFT] > 0) {
+                       if (ci->i_nr_by_mode[WR_SHIFT] >= FMODE_WAIT_BIAS ||
+                           time_after(ci->i_last_wr, used_cutoff))
+                               bits |= 1 << WR_SHIFT;
+               } else if (time_after(ci->i_last_wr, idle_cutoff)) {
+                       bits |= 1 << WR_SHIFT;
+               }
+
+               /* check lazyio only when read/write is wanted */
+               if ((bits & (CEPH_FILE_MODE_RDWR << 1)) &&
+                   ci->i_nr_by_mode[LAZY_SHIFT] > 0)
+                       bits |= 1 << LAZY_SHIFT;
+
+               return bits ? ceph_caps_for_mode(bits >> 1) : 0;
        }
-       if (bits == 0)
-               return 0;
-       return ceph_caps_for_mode(bits >> 1);
 }
 
 /*
@@ -1032,14 +1080,6 @@ int __ceph_caps_mds_wanted(struct ceph_inode_info *ci, bool check)
        return mds_wanted;
 }
 
-/*
- * called under i_ceph_lock
- */
-static int __ceph_is_single_caps(struct ceph_inode_info *ci)
-{
-       return rb_first(&ci->i_caps) == rb_last(&ci->i_caps);
-}
-
 int ceph_is_any_caps(struct inode *inode)
 {
        struct ceph_inode_info *ci = ceph_inode(inode);
@@ -1877,10 +1917,6 @@ void ceph_check_caps(struct ceph_inode_info *ci, int flags,
        if (ci->i_ceph_flags & CEPH_I_FLUSH)
                flags |= CHECK_CAPS_FLUSH;
 
-       if (!(flags & CHECK_CAPS_AUTHONLY) ||
-           (ci->i_auth_cap && __ceph_is_single_caps(ci)))
-               __cap_delay_cancel(mdsc, ci);
-
        goto retry_locked;
 retry:
        spin_lock(&ci->i_ceph_lock);
@@ -1907,9 +1943,7 @@ retry_locked:
                        if (IS_RDONLY(inode)) {
                                want = CEPH_CAP_ANY_SHARED;
                        } else {
-                               want = CEPH_CAP_ANY_SHARED |
-                                      CEPH_CAP_FILE_EXCL |
-                                      CEPH_CAP_ANY_DIR_OPS;
+                               want |= CEPH_CAP_ANY_SHARED | CEPH_CAP_FILE_EXCL;
                        }
                        retain |= want;
                } else {
@@ -2105,9 +2139,17 @@ ack:
                goto retry; /* retake i_ceph_lock and restart our cap scan. */
        }
 
-       /* Reschedule delayed caps release if we delayed anything */
-       if (delayed)
-               __cap_delay_requeue(mdsc, ci, false);
+       if (list_empty(&ci->i_cap_delay_list)) {
+           if (delayed) {
+                   /* Reschedule delayed caps release if we delayed anything */
+                   __cap_delay_requeue(mdsc, ci, false);
+           } else if (__ceph_is_any_real_caps(ci) &&
+                       (file_wanted & ~CEPH_CAP_PIN) &&
+                       !(used & (CEPH_CAP_FILE_RD | CEPH_CAP_ANY_FILE_WR))) {
+                   /* periodically re-calculate caps wanted by open files */
+                   __cap_delay_requeue(mdsc, ci, true);
+           }
+       }
 
        spin_unlock(&ci->i_ceph_lock);
 
@@ -2573,8 +2615,9 @@ void ceph_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,
+       /* first 8 bits are reserved for CEPH_FILE_MODE_FOO */
+       NON_BLOCKING    = (1 << 8),
+       CHECK_FILELOCK  = (1 << 9),
 };
 
 static int try_get_cap_refs(struct inode *inode, int need, int want,
@@ -2584,7 +2627,6 @@ static int try_get_cap_refs(struct inode *inode, int need, int want,
        struct ceph_mds_client *mdsc = ceph_inode_to_client(inode)->mdsc;
        int ret = 0;
        int have, implemented;
-       int file_wanted;
        bool snap_rwsem_locked = false;
 
        dout("get_cap_refs %p need %s want %s\n", inode,
@@ -2600,15 +2642,6 @@ again:
                goto out_unlock;
        }
 
-       /* make sure file is actually open */
-       file_wanted = __ceph_caps_file_wanted(ci);
-       if ((file_wanted & need) != need) {
-               dout("try_get_cap_refs need %s file_wanted %s, EBADF\n",
-                    ceph_cap_string(need), ceph_cap_string(file_wanted));
-               ret = -EBADF;
-               goto out_unlock;
-       }
-
        /* finish pending truncate */
        while (ci->i_truncate_pending) {
                spin_unlock(&ci->i_ceph_lock);
@@ -2719,6 +2752,9 @@ again:
                     ceph_cap_string(have), ceph_cap_string(need));
        }
 out_unlock:
+
+       __ceph_touch_fmode(ci, mdsc, flags);
+
        spin_unlock(&ci->i_ceph_lock);
        if (snap_rwsem_locked)
                up_read(&mdsc->snap_rwsem);
@@ -2756,10 +2792,20 @@ static void check_max_size(struct inode *inode, loff_t endoff)
                ceph_check_caps(ci, CHECK_CAPS_AUTHONLY, NULL);
 }
 
+static inline int get_used_fmode(int caps)
+{
+       int fmode = 0;
+       if (caps & CEPH_CAP_FILE_RD)
+               fmode |= CEPH_FILE_MODE_RD;
+       if (caps & CEPH_CAP_FILE_WR)
+               fmode |= CEPH_FILE_MODE_WR;
+       return fmode;
+}
+
 int ceph_try_get_caps(struct inode *inode, int need, int want,
                      bool nonblock, int *got)
 {
-       int ret;
+       int ret, flags;
 
        BUG_ON(need & ~CEPH_CAP_FILE_RD);
        BUG_ON(want & ~(CEPH_CAP_FILE_CACHE | CEPH_CAP_FILE_LAZYIO |
@@ -2771,8 +2817,11 @@ int ceph_try_get_caps(struct inode *inode, int need, int want,
                        return ret;
        }
 
-       ret = try_get_cap_refs(inode, need, want, 0,
-                              (nonblock ? NON_BLOCKING : 0), got);
+       flags = get_used_fmode(need | want);
+       if (nonblock)
+               flags |= NON_BLOCKING;
+
+       ret = try_get_cap_refs(inode, need, want, 0, flags, got);
        return ret == -EAGAIN ? 0 : ret;
 }
 
@@ -2798,11 +2847,15 @@ int ceph_get_caps(struct file *filp, int need, int want,
            fi->filp_gen != READ_ONCE(fsc->filp_gen))
                return -EBADF;
 
+       flags = get_used_fmode(need | want);
+
        while (true) {
                if (endoff > 0)
                        check_max_size(inode, endoff);
 
-               flags = atomic_read(&fi->num_locks) ? CHECK_FILELOCK : 0;
+               flags &= CEPH_FILE_MODE_MASK;
+               if (atomic_read(&fi->num_locks))
+                       flags |= CHECK_FILELOCK;
                _got = 0;
                ret = try_get_cap_refs(inode, need, want, endoff,
                                       flags, &_got);
@@ -2822,6 +2875,8 @@ int ceph_get_caps(struct file *filp, int need, int want,
                        list_add(&cw.list, &mdsc->cap_wait_list);
                        spin_unlock(&mdsc->caps_list_lock);
 
+                       /* make sure used fmode not timeout */
+                       ceph_get_fmode(ci, flags, FMODE_WAIT_BIAS);
                        add_wait_queue(&ci->i_cap_wq, &wait);
 
                        flags |= NON_BLOCKING;
@@ -2835,6 +2890,7 @@ int ceph_get_caps(struct file *filp, int need, int want,
                        }
 
                        remove_wait_queue(&ci->i_cap_wq, &wait);
+                       ceph_put_fmode(ci, flags, FMODE_WAIT_BIAS);
 
                        spin_lock(&mdsc->caps_list_lock);
                        list_del(&cw.list);
@@ -2854,7 +2910,7 @@ int ceph_get_caps(struct file *filp, int need, int want,
                if (ret < 0) {
                        if (ret == -ESTALE) {
                                /* session was killed, try renew caps */
-                               ret = ceph_renew_caps(inode);
+                               ret = ceph_renew_caps(inode, flags);
                                if (ret == 0)
                                        continue;
                        }
@@ -4153,6 +4209,33 @@ void ceph_flush_dirty_caps(struct ceph_mds_client *mdsc)
        dout("flush_dirty_caps done\n");
 }
 
+void __ceph_touch_fmode(struct ceph_inode_info *ci,
+                       struct ceph_mds_client *mdsc, int fmode)
+{
+       unsigned long now = jiffies;
+       if (fmode & CEPH_FILE_MODE_RD)
+               ci->i_last_rd = now;
+       if (fmode & CEPH_FILE_MODE_WR)
+               ci->i_last_wr = now;
+       /* queue periodic check */
+       if (fmode &&
+           __ceph_is_any_real_caps(ci) &&
+           list_empty(&ci->i_cap_delay_list))
+               __cap_delay_requeue(mdsc, ci, true);
+}
+
+void ceph_get_fmode(struct ceph_inode_info *ci, int fmode, int count)
+{
+       int i;
+       int bits = (fmode << 1) | 1;
+       spin_lock(&ci->i_ceph_lock);
+       for (i = 0; i < CEPH_FILE_MODE_BITS; i++) {
+               if (bits & (1 << i))
+                       ci->i_nr_by_mode[i] += count;
+       }
+       spin_unlock(&ci->i_ceph_lock);
+}
+
 void __ceph_get_fmode(struct ceph_inode_info *ci, int fmode)
 {
        int i;
@@ -4168,26 +4251,18 @@ void __ceph_get_fmode(struct ceph_inode_info *ci, int fmode)
  * we may need to release capabilities to the MDS (or schedule
  * their delayed release).
  */
-void ceph_put_fmode(struct ceph_inode_info *ci, int fmode)
+void ceph_put_fmode(struct ceph_inode_info *ci, int fmode, int count)
 {
-       int i, last = 0;
+       int i;
        int bits = (fmode << 1) | 1;
        spin_lock(&ci->i_ceph_lock);
        for (i = 0; i < CEPH_FILE_MODE_BITS; i++) {
                if (bits & (1 << i)) {
-                       BUG_ON(ci->i_nr_by_mode[i] == 0);
-                       if (--ci->i_nr_by_mode[i] == 0)
-                               last++;
+                       BUG_ON(ci->i_nr_by_mode[i] < count);
+                       ci->i_nr_by_mode[i] -= count;
                }
        }
-       dout("put_fmode %p fmode %d {%d,%d,%d,%d}\n",
-            &ci->vfs_inode, fmode,
-            ci->i_nr_by_mode[0], ci->i_nr_by_mode[1],
-            ci->i_nr_by_mode[2], ci->i_nr_by_mode[3]);
        spin_unlock(&ci->i_ceph_lock);
-
-       if (last && ci->i_vino.snap == CEPH_NOSNAP)
-               ceph_check_caps(ci, 0, NULL);
 }
 
 /*
index ee6b319..d594c26 100644 (file)
@@ -335,8 +335,11 @@ static int ceph_readdir(struct file *file, struct dir_context *ctx)
                ctx->pos = 2;
        }
 
-       /* can we use the dcache? */
        spin_lock(&ci->i_ceph_lock);
+       /* request Fx cap. if have Fx, we don't need to release Fs cap
+        * for later create/unlink. */
+       __ceph_touch_fmode(ci, mdsc, CEPH_FILE_MODE_WR);
+       /* can we use the dcache? */
        if (ceph_test_mount_opt(fsc, DCACHE) &&
            !ceph_test_mount_opt(fsc, NOASYNCREADDIR) &&
            ceph_snap(inode) != CEPH_SNAPDIR &&
@@ -760,6 +763,7 @@ static struct dentry *ceph_lookup(struct inode *dir, struct dentry *dentry,
                    ceph_test_mount_opt(fsc, DCACHE) &&
                    __ceph_dir_is_complete(ci) &&
                    (__ceph_caps_issued_mask(ci, CEPH_CAP_FILE_SHARED, 1))) {
+                       __ceph_touch_fmode(ci, mdsc, CEPH_FILE_MODE_RD);
                        spin_unlock(&ci->i_ceph_lock);
                        dout(" dir %p complete, -ENOENT\n", dir);
                        d_add(dentry, NULL);
@@ -1621,7 +1625,8 @@ static int __dir_lease_try_check(const struct dentry *dentry)
 /*
  * Check if directory-wide content lease/cap is valid.
  */
-static int dir_lease_is_valid(struct inode *dir, struct dentry *dentry)
+static int dir_lease_is_valid(struct inode *dir, struct dentry *dentry,
+                             struct ceph_mds_client *mdsc)
 {
        struct ceph_inode_info *ci = ceph_inode(dir);
        int valid;
@@ -1629,7 +1634,10 @@ static int dir_lease_is_valid(struct inode *dir, struct dentry *dentry)
 
        spin_lock(&ci->i_ceph_lock);
        valid = __ceph_caps_issued_mask(ci, CEPH_CAP_FILE_SHARED, 1);
-       shared_gen = atomic_read(&ci->i_shared_gen);
+       if (valid) {
+               __ceph_touch_fmode(ci, mdsc, CEPH_FILE_MODE_RD);
+               shared_gen = atomic_read(&ci->i_shared_gen);
+       }
        spin_unlock(&ci->i_ceph_lock);
        if (valid) {
                struct ceph_dentry_info *di;
@@ -1655,6 +1663,7 @@ static int ceph_d_revalidate(struct dentry *dentry, unsigned int flags)
        int valid = 0;
        struct dentry *parent;
        struct inode *dir, *inode;
+       struct ceph_mds_client *mdsc;
 
        if (flags & LOOKUP_RCU) {
                parent = READ_ONCE(dentry->d_parent);
@@ -1671,6 +1680,8 @@ static int ceph_d_revalidate(struct dentry *dentry, unsigned int flags)
        dout("d_revalidate %p '%pd' inode %p offset 0x%llx\n", dentry,
             dentry, inode, ceph_dentry(dentry)->offset);
 
+       mdsc = ceph_sb_to_client(dir->i_sb)->mdsc;
+
        /* always trust cached snapped dentries, snapdir dentry */
        if (ceph_snap(dir) != CEPH_NOSNAP) {
                dout("d_revalidate %p '%pd' inode %p is SNAPPED\n", dentry,
@@ -1682,7 +1693,7 @@ static int ceph_d_revalidate(struct dentry *dentry, unsigned int flags)
                valid = dentry_lease_is_valid(dentry, flags);
                if (valid == -ECHILD)
                        return valid;
-               if (valid || dir_lease_is_valid(dir, dentry)) {
+               if (valid || dir_lease_is_valid(dir, dentry, mdsc)) {
                        if (inode)
                                valid = ceph_is_any_caps(inode);
                        else
@@ -1691,8 +1702,6 @@ static int ceph_d_revalidate(struct dentry *dentry, unsigned int flags)
        }
 
        if (!valid) {
-               struct ceph_mds_client *mdsc =
-                       ceph_sb_to_client(dir->i_sb)->mdsc;
                struct ceph_mds_request *req;
                int op, err;
                u32 mask;
index a6c4841..6239cff 100644 (file)
@@ -213,7 +213,7 @@ static int ceph_init_file_info(struct inode *inode, struct file *file,
                struct ceph_dir_file_info *dfi =
                        kmem_cache_zalloc(ceph_dir_file_cachep, GFP_KERNEL);
                if (!dfi) {
-                       ceph_put_fmode(ci, fmode); /* clean up */
+                       ceph_put_fmode(ci, fmode, 1); /* clean up */
                        return -ENOMEM;
                }
 
@@ -224,7 +224,7 @@ static int ceph_init_file_info(struct inode *inode, struct file *file,
        } else {
                fi = kmem_cache_zalloc(ceph_file_cachep, GFP_KERNEL);
                if (!fi) {
-                       ceph_put_fmode(ci, fmode); /* clean up */
+                       ceph_put_fmode(ci, fmode, 1); /* clean up */
                        return -ENOMEM;
                }
 
@@ -263,7 +263,7 @@ static int ceph_init_file(struct inode *inode, struct file *file, int fmode)
        case S_IFLNK:
                dout("init_file %p %p 0%o (symlink)\n", inode, file,
                     inode->i_mode);
-               ceph_put_fmode(ceph_inode(inode), fmode); /* clean up */
+               ceph_put_fmode(ceph_inode(inode), fmode, 1); /* clean up */
                break;
 
        default:
@@ -273,7 +273,7 @@ static int ceph_init_file(struct inode *inode, struct file *file, int fmode)
                 * we need to drop the open ref now, since we don't
                 * have .release set to ceph_release.
                 */
-               ceph_put_fmode(ceph_inode(inode), fmode); /* clean up */
+               ceph_put_fmode(ceph_inode(inode), fmode, 1); /* clean up */
                BUG_ON(inode->i_fop->release == ceph_release);
 
                /* call the proper open fop */
@@ -285,14 +285,15 @@ static int ceph_init_file(struct inode *inode, struct file *file, int fmode)
 /*
  * try renew caps after session gets killed.
  */
-int ceph_renew_caps(struct inode *inode)
+int ceph_renew_caps(struct inode *inode, int fmode)
 {
-       struct ceph_mds_client *mdsc = ceph_sb_to_client(inode->i_sb)->mdsc;
+       struct ceph_mds_client *mdsc = ceph_inode_to_client(inode)->mdsc;
        struct ceph_inode_info *ci = ceph_inode(inode);
        struct ceph_mds_request *req;
        int err, flags, wanted;
 
        spin_lock(&ci->i_ceph_lock);
+       __ceph_touch_fmode(ci, mdsc, fmode);
        wanted = __ceph_caps_file_wanted(ci);
        if (__ceph_is_any_real_caps(ci) &&
            (!(wanted & CEPH_CAP_ANY_WR) || ci->i_auth_cap)) {
@@ -405,6 +406,7 @@ int ceph_open(struct inode *inode, struct file *file)
        } else if (ceph_snap(inode) != CEPH_NOSNAP &&
                   (ci->i_snap_caps & wanted) == wanted) {
                __ceph_get_fmode(ci, fmode);
+               __ceph_touch_fmode(ci, mdsc, fmode);
                spin_unlock(&ci->i_ceph_lock);
                return ceph_init_file(inode, file, fmode);
        }
@@ -781,7 +783,7 @@ retry:
        }
 out_req:
        if (!req->r_err && req->r_target_inode)
-               ceph_put_fmode(ceph_inode(req->r_target_inode), req->r_fmode);
+               ceph_put_fmode(ceph_inode(req->r_target_inode), req->r_fmode, 1);
        ceph_mdsc_put_request(req);
 out_ctx:
        ceph_release_acl_sec_ctx(&as_ctx);
@@ -798,7 +800,7 @@ int ceph_release(struct inode *inode, struct file *file)
                dout("release inode %p dir file %p\n", inode, file);
                WARN_ON(!list_empty(&dfi->file_info.rw_contexts));
 
-               ceph_put_fmode(ci, dfi->file_info.fmode);
+               ceph_put_fmode(ci, dfi->file_info.fmode, 1);
 
                if (dfi->last_readdir)
                        ceph_mdsc_put_request(dfi->last_readdir);
@@ -810,7 +812,8 @@ int ceph_release(struct inode *inode, struct file *file)
                dout("release inode %p regular file %p\n", inode, file);
                WARN_ON(!list_empty(&fi->rw_contexts));
 
-               ceph_put_fmode(ci, fi->fmode);
+               ceph_put_fmode(ci, fi->fmode, 1);
+
                kmem_cache_free(ceph_file_cachep, fi);
        }
 
index 73f986e..a618e8a 100644 (file)
@@ -479,6 +479,7 @@ struct inode *ceph_alloc_inode(struct super_block *sb)
        ci->i_head_snapc = NULL;
        ci->i_snap_caps = 0;
 
+       ci->i_last_rd = ci->i_last_wr = jiffies - 3600 * HZ;
        for (i = 0; i < CEPH_FILE_MODE_BITS; i++)
                ci->i_nr_by_mode[i] = 0;
 
@@ -639,7 +640,7 @@ int ceph_fill_file_size(struct inode *inode, int issued,
                        if ((issued & (CEPH_CAP_FILE_CACHE|
                                       CEPH_CAP_FILE_BUFFER)) ||
                            mapping_mapped(inode->i_mapping) ||
-                           __ceph_caps_file_wanted(ci)) {
+                           __ceph_is_file_opened(ci)) {
                                ci->i_truncate_pending++;
                                queue_trunc = 1;
                        }
@@ -1013,6 +1014,13 @@ int ceph_fill_inode(struct inode *inode, struct page *locked_page,
                        fill_inline = true;
        }
 
+       if (cap_fmode >= 0) {
+               if (!info_caps)
+                       pr_warn("mds issued no caps on %llx.%llx\n",
+                               ceph_vinop(inode));
+               __ceph_touch_fmode(ci, mdsc, cap_fmode);
+       }
+
        spin_unlock(&ci->i_ceph_lock);
 
        if (fill_inline)
index c90f03b..6e061bf 100644 (file)
@@ -243,11 +243,13 @@ static long ceph_ioctl_lazyio(struct file *file)
        struct ceph_file_info *fi = file->private_data;
        struct inode *inode = file_inode(file);
        struct ceph_inode_info *ci = ceph_inode(inode);
+       struct ceph_mds_client *mdsc = ceph_inode_to_client(inode)->mdsc;
 
        if ((fi->fmode & CEPH_FILE_MODE_LAZY) == 0) {
                spin_lock(&ci->i_ceph_lock);
                fi->fmode |= CEPH_FILE_MODE_LAZY;
                ci->i_nr_by_mode[ffs(CEPH_FILE_MODE_LAZY)]++;
+               __ceph_touch_fmode(ci, mdsc, fi->fmode);
                spin_unlock(&ci->i_ceph_lock);
                dout("ioctl_layzio: file %p marked lazy\n", file);
 
index 68b8afd..486f91f 100644 (file)
@@ -2486,7 +2486,8 @@ static struct ceph_msg *create_request_message(struct ceph_mds_client *mdsc,
        if (req->r_inode_drop)
                releases += ceph_encode_inode_release(&p,
                      req->r_inode ? req->r_inode : d_inode(req->r_dentry),
-                     mds, req->r_inode_drop, req->r_inode_unless, 0);
+                     mds, req->r_inode_drop, req->r_inode_unless,
+                     req->r_op == CEPH_MDS_OP_READDIR);
        if (req->r_dentry_drop)
                releases += ceph_encode_dentry_release(&p, req->r_dentry,
                                req->r_parent, mds, req->r_dentry_drop,
@@ -2833,7 +2834,13 @@ int ceph_mdsc_submit_request(struct ceph_mds_client *mdsc, struct inode *dir,
        if (req->r_inode)
                ceph_get_cap_refs(ceph_inode(req->r_inode), CEPH_CAP_PIN);
        if (req->r_parent) {
-               ceph_get_cap_refs(ceph_inode(req->r_parent), CEPH_CAP_PIN);
+               struct ceph_inode_info *ci = ceph_inode(req->r_parent);
+               int fmode = (req->r_op & CEPH_MDS_OP_WRITE) ?
+                           CEPH_FILE_MODE_WR : CEPH_FILE_MODE_RD;
+               spin_lock(&ci->i_ceph_lock);
+               ceph_take_cap_refs(ci, CEPH_CAP_PIN, false);
+               __ceph_touch_fmode(ci, mdsc, fmode);
+               spin_unlock(&ci->i_ceph_lock);
                ihold(req->r_parent);
        }
        if (req->r_old_dentry_dir)
index 356ad7b..63dd8e6 100644 (file)
@@ -366,6 +366,8 @@ struct ceph_inode_info {
                                                    dirty|flushing caps */
        unsigned i_snap_caps;           /* cap bits for snapped files */
 
+       unsigned long i_last_rd;
+       unsigned long i_last_wr;
        int i_nr_by_mode[CEPH_FILE_MODE_BITS];  /* open file counts */
 
        struct mutex i_truncate_mutex;
@@ -680,6 +682,10 @@ extern int __ceph_caps_revoking_other(struct ceph_inode_info *ci,
 extern int ceph_caps_revoking(struct ceph_inode_info *ci, int mask);
 extern int __ceph_caps_used(struct ceph_inode_info *ci);
 
+static inline bool __ceph_is_file_opened(struct ceph_inode_info *ci)
+{
+       return ci->i_nr_by_mode[0];
+}
 extern int __ceph_caps_file_wanted(struct ceph_inode_info *ci);
 extern int __ceph_caps_wanted(struct ceph_inode_info *ci);
 
@@ -1093,7 +1099,10 @@ extern int ceph_try_get_caps(struct inode *inode,
 
 /* for counting open files by mode */
 extern void __ceph_get_fmode(struct ceph_inode_info *ci, int mode);
-extern void ceph_put_fmode(struct ceph_inode_info *ci, int mode);
+extern void ceph_get_fmode(struct ceph_inode_info *ci, int mode, int count);
+extern void ceph_put_fmode(struct ceph_inode_info *ci, int mode, int count);
+extern void __ceph_touch_fmode(struct ceph_inode_info *ci,
+                              struct ceph_mds_client *mdsc, int fmode);
 
 /* addr.c */
 extern const struct address_space_operations ceph_aops;
@@ -1105,7 +1114,7 @@ extern void ceph_pool_perm_destroy(struct ceph_mds_client* mdsc);
 /* file.c */
 extern const struct file_operations ceph_file_fops;
 
-extern int ceph_renew_caps(struct inode *inode);
+extern int ceph_renew_caps(struct inode *inode, int fmode);
 extern int ceph_open(struct inode *inode, struct file *file);
 extern int ceph_atomic_open(struct inode *dir, struct dentry *dentry,
                            struct file *file, unsigned flags, umode_t mode);
index e035c51..ebf5ba6 100644 (file)
@@ -568,6 +568,7 @@ struct ceph_filelock {
 #define CEPH_FILE_MODE_RDWR       3  /* RD | WR */
 #define CEPH_FILE_MODE_LAZY       4  /* lazy io */
 #define CEPH_FILE_MODE_BITS       4
+#define CEPH_FILE_MODE_MASK       ((1 << CEPH_FILE_MODE_BITS) - 1)
 
 int ceph_flags_to_mode(int flags);