|
@@ -1080,6 +1080,27 @@ static struct dentry *splice_dentry(struct dentry *dn, struct inode *in)
|
|
|
|
|
|
BUG_ON(d_inode(dn));
|
|
BUG_ON(d_inode(dn));
|
|
|
|
|
|
|
|
+ if (S_ISDIR(in->i_mode)) {
|
|
|
|
+ /* If inode is directory, d_splice_alias() below will remove
|
|
|
|
+ * 'realdn' from its origin parent. We need to ensure that
|
|
|
|
+ * origin parent's readdir cache will not reference 'realdn'
|
|
|
|
+ */
|
|
|
|
+ realdn = d_find_any_alias(in);
|
|
|
|
+ if (realdn) {
|
|
|
|
+ struct ceph_dentry_info *di = ceph_dentry(realdn);
|
|
|
|
+ spin_lock(&realdn->d_lock);
|
|
|
|
+
|
|
|
|
+ realdn->d_op->d_prune(realdn);
|
|
|
|
+
|
|
|
|
+ di->time = jiffies;
|
|
|
|
+ di->lease_shared_gen = 0;
|
|
|
|
+ di->offset = 0;
|
|
|
|
+
|
|
|
|
+ spin_unlock(&realdn->d_lock);
|
|
|
|
+ dput(realdn);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
/* dn must be unhashed */
|
|
/* dn must be unhashed */
|
|
if (!d_unhashed(dn))
|
|
if (!d_unhashed(dn))
|
|
d_drop(dn);
|
|
d_drop(dn);
|
|
@@ -1295,8 +1316,8 @@ retry_lookup:
|
|
if (!rinfo->head->is_target) {
|
|
if (!rinfo->head->is_target) {
|
|
dout("fill_trace null dentry\n");
|
|
dout("fill_trace null dentry\n");
|
|
if (d_really_is_positive(dn)) {
|
|
if (d_really_is_positive(dn)) {
|
|
- ceph_dir_clear_ordered(dir);
|
|
|
|
dout("d_delete %p\n", dn);
|
|
dout("d_delete %p\n", dn);
|
|
|
|
+ ceph_dir_clear_ordered(dir);
|
|
d_delete(dn);
|
|
d_delete(dn);
|
|
} else if (have_lease) {
|
|
} else if (have_lease) {
|
|
if (d_unhashed(dn))
|
|
if (d_unhashed(dn))
|
|
@@ -1323,7 +1344,6 @@ retry_lookup:
|
|
dout(" %p links to %p %llx.%llx, not %llx.%llx\n",
|
|
dout(" %p links to %p %llx.%llx, not %llx.%llx\n",
|
|
dn, d_inode(dn), ceph_vinop(d_inode(dn)),
|
|
dn, d_inode(dn), ceph_vinop(d_inode(dn)),
|
|
ceph_vinop(in));
|
|
ceph_vinop(in));
|
|
- ceph_dir_clear_ordered(dir);
|
|
|
|
d_invalidate(dn);
|
|
d_invalidate(dn);
|
|
have_lease = false;
|
|
have_lease = false;
|
|
}
|
|
}
|
|
@@ -1573,9 +1593,19 @@ retry_lookup:
|
|
} else if (d_really_is_positive(dn) &&
|
|
} else if (d_really_is_positive(dn) &&
|
|
(ceph_ino(d_inode(dn)) != tvino.ino ||
|
|
(ceph_ino(d_inode(dn)) != tvino.ino ||
|
|
ceph_snap(d_inode(dn)) != tvino.snap)) {
|
|
ceph_snap(d_inode(dn)) != tvino.snap)) {
|
|
|
|
+ struct ceph_dentry_info *di = ceph_dentry(dn);
|
|
dout(" dn %p points to wrong inode %p\n",
|
|
dout(" dn %p points to wrong inode %p\n",
|
|
dn, d_inode(dn));
|
|
dn, d_inode(dn));
|
|
- __ceph_dir_clear_ordered(ci);
|
|
|
|
|
|
+
|
|
|
|
+ spin_lock(&dn->d_lock);
|
|
|
|
+ if (di->offset > 0 &&
|
|
|
|
+ di->lease_shared_gen ==
|
|
|
|
+ atomic_read(&ci->i_shared_gen)) {
|
|
|
|
+ __ceph_dir_clear_ordered(ci);
|
|
|
|
+ di->offset = 0;
|
|
|
|
+ }
|
|
|
|
+ spin_unlock(&dn->d_lock);
|
|
|
|
+
|
|
d_delete(dn);
|
|
d_delete(dn);
|
|
dput(dn);
|
|
dput(dn);
|
|
goto retry_lookup;
|
|
goto retry_lookup;
|
|
@@ -1600,9 +1630,7 @@ retry_lookup:
|
|
&req->r_caps_reservation);
|
|
&req->r_caps_reservation);
|
|
if (ret < 0) {
|
|
if (ret < 0) {
|
|
pr_err("fill_inode badness on %p\n", in);
|
|
pr_err("fill_inode badness on %p\n", in);
|
|
- if (d_really_is_positive(dn))
|
|
|
|
- __ceph_dir_clear_ordered(ci);
|
|
|
|
- else
|
|
|
|
|
|
+ if (d_really_is_negative(dn))
|
|
iput(in);
|
|
iput(in);
|
|
d_drop(dn);
|
|
d_drop(dn);
|
|
err = ret;
|
|
err = ret;
|