ceph: allow dentry_lease_is_valid to work under RCU walk
Under rcuwalk, we need to take extra care when dereferencing d_parent. We want to do that once and pass a pointer to dentry_lease_is_valid. Also, we must ensure that that function can handle the case where we're racing with d_release. Check whether "di" is NULL under the d_lock, and just return 0 if so. Finally, we still need to kick off a renewal job if the lease is getting close to expiration. If that's the case, then just drop out of rcuwalk mode since that could block. Signed-off-by: Jeff Layton <jlayton@redhat.com> Reviewed-by: Yan, Zheng <zyan@redhat.com>
This commit is contained in:
parent
5b484a5131
commit
14fb9c9efe
@ -1133,7 +1133,8 @@ 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 int dentry_lease_is_valid(struct dentry *dentry)
|
||||
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;
|
||||
@ -1141,12 +1142,11 @@ static int dentry_lease_is_valid(struct dentry *dentry)
|
||||
u32 gen;
|
||||
unsigned long ttl;
|
||||
struct ceph_mds_session *session = NULL;
|
||||
struct inode *dir = NULL;
|
||||
u32 seq = 0;
|
||||
|
||||
spin_lock(&dentry->d_lock);
|
||||
di = ceph_dentry(dentry);
|
||||
if (di->lease_session) {
|
||||
if (di && di->lease_session) {
|
||||
s = di->lease_session;
|
||||
spin_lock(&s->s_gen_ttl_lock);
|
||||
gen = s->s_cap_gen;
|
||||
@ -1159,12 +1159,19 @@ static int dentry_lease_is_valid(struct dentry *dentry)
|
||||
valid = 1;
|
||||
if (di->lease_renew_after &&
|
||||
time_after(jiffies, di->lease_renew_after)) {
|
||||
/* we should renew */
|
||||
dir = d_inode(dentry->d_parent);
|
||||
session = ceph_get_mds_session(s);
|
||||
seq = di->lease_seq;
|
||||
di->lease_renew_after = 0;
|
||||
di->lease_renew_from = jiffies;
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1224,12 +1231,16 @@ static int ceph_d_revalidate(struct dentry *dentry, unsigned int flags)
|
||||
} else if (d_really_is_positive(dentry) &&
|
||||
ceph_snap(d_inode(dentry)) == CEPH_SNAPDIR) {
|
||||
valid = 1;
|
||||
} else if (dentry_lease_is_valid(dentry) ||
|
||||
dir_lease_is_valid(dir, dentry)) {
|
||||
if (d_really_is_positive(dentry))
|
||||
valid = ceph_is_any_caps(d_inode(dentry));
|
||||
else
|
||||
valid = 1;
|
||||
} else {
|
||||
valid = dentry_lease_is_valid(dentry, flags, dir);
|
||||
if (valid == -ECHILD)
|
||||
return valid;
|
||||
if (valid || dir_lease_is_valid(dir, dentry)) {
|
||||
if (d_really_is_positive(dentry))
|
||||
valid = ceph_is_any_caps(d_inode(dentry));
|
||||
else
|
||||
valid = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!valid) {
|
||||
|
Loading…
Reference in New Issue
Block a user