ceph: delete stale dentry when last reference is dropped
authorYan, Zheng <zyan@redhat.com>
Mon, 28 Jan 2019 12:43:55 +0000 (20:43 +0800)
committerIlya Dryomov <idryomov@gmail.com>
Tue, 5 Mar 2019 17:55:17 +0000 (18:55 +0100)
introduce ceph_d_delete(), which checks if dentry has valid lease.

Signed-off-by: "Yan, Zheng" <zyan@redhat.com>
Reviewed-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
fs/ceph/dir.c
fs/ceph/inode.c
fs/ceph/mds_client.c
fs/ceph/super.h

index 82928ce..f34a57f 100644 (file)
@@ -1139,45 +1139,59 @@ void ceph_invalidate_dentry_lease(struct dentry *dentry)
  * Check if dentry lease is valid.  If not, delete the lease.  Try to
  * renew if the least is more than half up.
  */
+static bool __dentry_lease_is_valid(struct ceph_dentry_info *di)
+{
+       struct ceph_mds_session *session;
+
+       if (!di->lease_gen)
+               return false;
+
+       session = di->lease_session;
+       if (session) {
+               u32 gen;
+               unsigned long ttl;
+
+               spin_lock(&session->s_gen_ttl_lock);
+               gen = session->s_cap_gen;
+               ttl = session->s_cap_ttl;
+               spin_unlock(&session->s_gen_ttl_lock);
+
+               if (di->lease_gen == gen &&
+                   time_before(jiffies, ttl) &&
+                   time_before(jiffies, di->time))
+                       return true;
+       }
+       di->lease_gen = 0;
+       return false;
+}
+
 static int dentry_lease_is_valid(struct dentry *dentry, unsigned int flags,
                                 struct inode *dir)
 {
        struct ceph_dentry_info *di;
-       struct ceph_mds_session *s;
-       int valid = 0;
-       u32 gen;
-       unsigned long ttl;
        struct ceph_mds_session *session = NULL;
        u32 seq = 0;
+       int valid = 0;
 
        spin_lock(&dentry->d_lock);
        di = ceph_dentry(dentry);
-       if (di && di->lease_session) {
-               s = di->lease_session;
-               spin_lock(&s->s_gen_ttl_lock);
-               gen = s->s_cap_gen;
-               ttl = s->s_cap_ttl;
-               spin_unlock(&s->s_gen_ttl_lock);
+       if (di && __dentry_lease_is_valid(di)) {
+               valid = 1;
 
-               if (di->lease_gen == gen &&
-                   time_before(jiffies, di->time) &&
-                   time_before(jiffies, ttl)) {
-                       valid = 1;
-                       if (di->lease_renew_after &&
-                           time_after(jiffies, di->lease_renew_after)) {
-                               /*
-                                * We should renew. If we're in RCU walk mode
-                                * though, we can't do that so just return
-                                * -ECHILD.
-                                */
-                               if (flags & LOOKUP_RCU) {
-                                       valid = -ECHILD;
-                               } else {
-                                       session = ceph_get_mds_session(s);
-                                       seq = di->lease_seq;
-                                       di->lease_renew_after = 0;
-                                       di->lease_renew_from = jiffies;
-                               }
+               if (di->lease_renew_after &&
+                   time_after(jiffies, di->lease_renew_after)) {
+                       /*
+                        * We should renew. If we're in RCU walk mode
+                        * though, we can't do that so just return
+                        * -ECHILD.
+                        */
+                       if (flags & LOOKUP_RCU) {
+                               valid = -ECHILD;
+                       } else {
+                               session = ceph_get_mds_session(di->lease_session);
+                               seq = di->lease_seq;
+                               di->lease_renew_after = 0;
+                               di->lease_renew_from = jiffies;
                        }
                }
        }
@@ -1192,6 +1206,38 @@ static int dentry_lease_is_valid(struct dentry *dentry, unsigned int flags,
        return valid;
 }
 
+/*
+ * Called under dentry->d_lock.
+ */
+static int __dir_lease_try_check(const struct dentry *dentry)
+{
+       struct ceph_dentry_info *di = ceph_dentry(dentry);
+       struct inode *dir;
+       struct ceph_inode_info *ci;
+       int valid = 0;
+
+       if (!di->lease_shared_gen)
+               return 0;
+       if (IS_ROOT(dentry))
+               return 0;
+
+       dir = d_inode(dentry->d_parent);
+       ci = ceph_inode(dir);
+
+       if (spin_trylock(&ci->i_ceph_lock)) {
+               if (atomic_read(&ci->i_shared_gen) == di->lease_shared_gen &&
+                   __ceph_caps_issued_mask(ci, CEPH_CAP_FILE_SHARED, 0))
+                       valid = 1;
+               spin_unlock(&ci->i_ceph_lock);
+       } else {
+               valid = -EBUSY;
+       }
+
+       if (!valid)
+               di->lease_shared_gen = 0;
+       return valid;
+}
+
 /*
  * Check if directory-wide content lease/cap is valid.
  */
@@ -1308,6 +1354,31 @@ static int ceph_d_revalidate(struct dentry *dentry, unsigned int flags)
        return valid;
 }
 
+/*
+ * Delete unused dentry that doesn't have valid lease
+ *
+ * Called under dentry->d_lock.
+ */
+static int ceph_d_delete(const struct dentry *dentry)
+{
+       struct ceph_dentry_info *di;
+
+       /* won't release caps */
+       if (d_really_is_negative(dentry))
+               return 0;
+       if (ceph_snap(d_inode(dentry)) != CEPH_NOSNAP)
+               return 0;
+       /* vaild lease? */
+       di = ceph_dentry(dentry);
+       if (di) {
+               if (__dentry_lease_is_valid(di))
+                       return 0;
+               if (__dir_lease_try_check(dentry))
+                       return 0;
+       }
+       return 1;
+}
+
 /*
  * Release our ceph_dentry_info.
  */
@@ -1531,6 +1602,7 @@ const struct inode_operations ceph_snapdir_iops = {
 
 const struct dentry_operations ceph_dentry_ops = {
        .d_revalidate = ceph_d_revalidate,
+       .d_delete = ceph_d_delete,
        .d_release = ceph_d_release,
        .d_prune = ceph_d_prune,
        .d_init = ceph_d_init,
index f588b2d..f75476e 100644 (file)
@@ -497,7 +497,7 @@ struct inode *ceph_alloc_inode(struct super_block *sb)
        ci->i_wrbuffer_ref = 0;
        ci->i_wrbuffer_ref_head = 0;
        atomic_set(&ci->i_filelock_ref, 0);
-       atomic_set(&ci->i_shared_gen, 0);
+       atomic_set(&ci->i_shared_gen, 1);
        ci->i_rdcache_gen = 0;
        ci->i_rdcache_revoking = 0;
 
index c9d4561..99d6e80 100644 (file)
@@ -621,7 +621,7 @@ static struct ceph_mds_session *register_session(struct ceph_mds_client *mdsc,
        ceph_con_init(&s->s_con, s, &mds_con_ops, &mdsc->fsc->client->msgr);
 
        spin_lock_init(&s->s_gen_ttl_lock);
-       s->s_cap_gen = 0;
+       s->s_cap_gen = 1;
        s->s_cap_ttl = jiffies - 1;
 
        spin_lock_init(&s->s_cap_lock);
index c4a79ea..c0654e6 100644 (file)
@@ -594,7 +594,7 @@ extern u32 ceph_choose_frag(struct ceph_inode_info *ci, u32 v,
                            struct ceph_inode_frag *pfrag,
                            int *found);
 
-static inline struct ceph_dentry_info *ceph_dentry(struct dentry *dentry)
+static inline struct ceph_dentry_info *ceph_dentry(const struct dentry *dentry)
 {
        return (struct ceph_dentry_info *)dentry->d_fsdata;
 }