[3/5] ceph: delete stale dentry when last reference is dropped
diff mbox series

Message ID 20190201142253.5478-4-zyan@redhat.com
State New
Headers show
Series
  • ceph: proactively release used caps
Related show

Commit Message

Yan, Zheng Feb. 1, 2019, 2:22 p.m. UTC
introduce ceph_d_delete(), which checks if dentry has valid lease.

Signed-off-by: "Yan, Zheng" <zyan@redhat.com>
---
 fs/ceph/dir.c        | 132 +++++++++++++++++++++++++++++++++----------
 fs/ceph/inode.c      |   2 +-
 fs/ceph/mds_client.c |   2 +-
 fs/ceph/super.h      |   2 +-
 4 files changed, 106 insertions(+), 32 deletions(-)

Comments

Jeff Layton Feb. 1, 2019, 2:49 p.m. UTC | #1
On Fri, 2019-02-01 at 22:22 +0800, Yan, Zheng wrote:
> introduce ceph_d_delete(), which checks if dentry has valid lease.
>
> Signed-off-by: "Yan, Zheng" <zyan@redhat.com>
> ---
>  fs/ceph/dir.c        | 132 +++++++++++++++++++++++++++++++++----------
>  fs/ceph/inode.c      |   2 +-
>  fs/ceph/mds_client.c |   2 +-
>  fs/ceph/super.h      |   2 +-
>  4 files changed, 106 insertions(+), 32 deletions(-)
> 
> diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c
> index 82928cea0209..bb5d765fcaae 100644
> --- a/fs/ceph/dir.c
> +++ b/fs/ceph/dir.c
> @@ -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,40 @@ 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;
> +
> +	rcu_read_lock();
> +	dir = d_inode_rcu(dentry->d_parent);
> +	ci = ceph_inode(dir);

If you hold dentry->d_lock (and you do here), then d_parent is stable
and you shouldn't need to use RCU.

> +
> +	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;
> +	}
> +	rcu_read_unlock();
> +
> +	if (!valid)
> +		di->lease_shared_gen = 0;
> +	return valid;
> +}
> +
>  /*
>   * Check if directory-wide content lease/cap is valid.
>   */
> @@ -1308,6 +1356,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 +1604,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,
> diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c
> index f588b2d7b598..f75476e94d75 100644
> --- a/fs/ceph/inode.c
> +++ b/fs/ceph/inode.c
> @@ -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;
>  
> diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c
> index 8f2d97e806b2..e5aea1db8d84 100644
> --- a/fs/ceph/mds_client.c
> +++ b/fs/ceph/mds_client.c
> @@ -619,7 +619,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);
> diff --git a/fs/ceph/super.h b/fs/ceph/super.h
> index c4a79eadc55a..c0654e613fc0 100644
> --- a/fs/ceph/super.h
> +++ b/fs/ceph/super.h
> @@ -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;
>  }

Patch
diff mbox series

diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c
index 82928cea0209..bb5d765fcaae 100644
--- a/fs/ceph/dir.c
+++ b/fs/ceph/dir.c
@@ -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,40 @@  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;
+
+	rcu_read_lock();
+	dir = d_inode_rcu(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;
+	}
+	rcu_read_unlock();
+
+	if (!valid)
+		di->lease_shared_gen = 0;
+	return valid;
+}
+
 /*
  * Check if directory-wide content lease/cap is valid.
  */
@@ -1308,6 +1356,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 +1604,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,
diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c
index f588b2d7b598..f75476e94d75 100644
--- a/fs/ceph/inode.c
+++ b/fs/ceph/inode.c
@@ -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;
 
diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c
index 8f2d97e806b2..e5aea1db8d84 100644
--- a/fs/ceph/mds_client.c
+++ b/fs/ceph/mds_client.c
@@ -619,7 +619,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);
diff --git a/fs/ceph/super.h b/fs/ceph/super.h
index c4a79eadc55a..c0654e613fc0 100644
--- a/fs/ceph/super.h
+++ b/fs/ceph/super.h
@@ -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;
 }