ceph: set dn offset when spliced
We want to assign an offset when the dentry goes from null to linked, which is always done by splice_dentry(). Notably, we should NOT assign an offset when a dentry is first created and is still null. BUG if we try to splice a non-null dentry (we shouldn't). Signed-off-by: Sage Weil <sage@newdream.net>
This commit is contained in:
parent
1b7facc41b
commit
1cd3935bed
@ -128,7 +128,8 @@ static int __dcache_readdir(struct file *filp,
|
|||||||
dentry = list_entry(p, struct dentry, d_u.d_child);
|
dentry = list_entry(p, struct dentry, d_u.d_child);
|
||||||
di = ceph_dentry(dentry);
|
di = ceph_dentry(dentry);
|
||||||
while (1) {
|
while (1) {
|
||||||
dout(" p %p/%p d_subdirs %p/%p\n", p->prev, p->next,
|
dout(" p %p/%p %s d_subdirs %p/%p\n", p->prev, p->next,
|
||||||
|
d_unhashed(dentry) ? "!hashed" : "hashed",
|
||||||
parent->d_subdirs.prev, parent->d_subdirs.next);
|
parent->d_subdirs.prev, parent->d_subdirs.next);
|
||||||
if (p == &parent->d_subdirs) {
|
if (p == &parent->d_subdirs) {
|
||||||
fi->at_end = 1;
|
fi->at_end = 1;
|
||||||
@ -571,7 +572,6 @@ static struct dentry *ceph_lookup(struct inode *dir, struct dentry *dentry,
|
|||||||
!is_root_ceph_dentry(dir, dentry) &&
|
!is_root_ceph_dentry(dir, dentry) &&
|
||||||
(ci->i_ceph_flags & CEPH_I_COMPLETE) &&
|
(ci->i_ceph_flags & CEPH_I_COMPLETE) &&
|
||||||
(__ceph_caps_issued_mask(ci, CEPH_CAP_FILE_SHARED, 1))) {
|
(__ceph_caps_issued_mask(ci, CEPH_CAP_FILE_SHARED, 1))) {
|
||||||
di->offset = ci->i_max_offset++;
|
|
||||||
spin_unlock(&dir->i_lock);
|
spin_unlock(&dir->i_lock);
|
||||||
dout(" dir %p complete, -ENOENT\n", dir);
|
dout(" dir %p complete, -ENOENT\n", dir);
|
||||||
d_add(dentry, NULL);
|
d_add(dentry, NULL);
|
||||||
@ -984,8 +984,9 @@ static int ceph_d_revalidate(struct dentry *dentry, struct nameidata *nd)
|
|||||||
{
|
{
|
||||||
struct inode *dir = dentry->d_parent->d_inode;
|
struct inode *dir = dentry->d_parent->d_inode;
|
||||||
|
|
||||||
dout("d_revalidate %p '%.*s' inode %p\n", dentry,
|
dout("d_revalidate %p '%.*s' inode %p offset %lld\n", dentry,
|
||||||
dentry->d_name.len, dentry->d_name.name, dentry->d_inode);
|
dentry->d_name.len, dentry->d_name.name, dentry->d_inode,
|
||||||
|
ceph_dentry(dentry)->offset);
|
||||||
|
|
||||||
/* always trust cached snapped dentries, snapdir dentry */
|
/* always trust cached snapped dentries, snapdir dentry */
|
||||||
if (ceph_snap(dir) != CEPH_NOSNAP) {
|
if (ceph_snap(dir) != CEPH_NOSNAP) {
|
||||||
@ -1177,8 +1178,8 @@ void ceph_dentry_lru_touch(struct dentry *dn)
|
|||||||
struct ceph_dentry_info *di = ceph_dentry(dn);
|
struct ceph_dentry_info *di = ceph_dentry(dn);
|
||||||
struct ceph_mds_client *mdsc;
|
struct ceph_mds_client *mdsc;
|
||||||
|
|
||||||
dout("dentry_lru_touch %p %p '%.*s'\n", di, dn,
|
dout("dentry_lru_touch %p %p '%.*s' (offset %lld)\n", di, dn,
|
||||||
dn->d_name.len, dn->d_name.name);
|
dn->d_name.len, dn->d_name.name, di->offset);
|
||||||
if (di) {
|
if (di) {
|
||||||
mdsc = &ceph_sb_to_client(dn->d_sb)->mdsc;
|
mdsc = &ceph_sb_to_client(dn->d_sb)->mdsc;
|
||||||
spin_lock(&mdsc->dentry_lru_lock);
|
spin_lock(&mdsc->dentry_lru_lock);
|
||||||
|
@ -803,50 +803,6 @@ static void update_dentry_lease(struct dentry *dentry,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* splice a dentry to an inode.
|
|
||||||
* caller must hold directory i_mutex for this to be safe.
|
|
||||||
*
|
|
||||||
* we will only rehash the resulting dentry if @prehash is
|
|
||||||
* true; @prehash will be set to false (for the benefit of
|
|
||||||
* the caller) if we fail.
|
|
||||||
*/
|
|
||||||
static struct dentry *splice_dentry(struct dentry *dn, struct inode *in,
|
|
||||||
bool *prehash)
|
|
||||||
{
|
|
||||||
struct dentry *realdn;
|
|
||||||
|
|
||||||
/* dn must be unhashed */
|
|
||||||
if (!d_unhashed(dn))
|
|
||||||
d_drop(dn);
|
|
||||||
realdn = d_materialise_unique(dn, in);
|
|
||||||
if (IS_ERR(realdn)) {
|
|
||||||
pr_err("splice_dentry error %p inode %p ino %llx.%llx\n",
|
|
||||||
dn, in, ceph_vinop(in));
|
|
||||||
if (prehash)
|
|
||||||
*prehash = false; /* don't rehash on error */
|
|
||||||
dn = realdn; /* note realdn contains the error */
|
|
||||||
goto out;
|
|
||||||
} else if (realdn) {
|
|
||||||
dout("dn %p (%d) spliced with %p (%d) "
|
|
||||||
"inode %p ino %llx.%llx\n",
|
|
||||||
dn, atomic_read(&dn->d_count),
|
|
||||||
realdn, atomic_read(&realdn->d_count),
|
|
||||||
realdn->d_inode, ceph_vinop(realdn->d_inode));
|
|
||||||
dput(dn);
|
|
||||||
dn = realdn;
|
|
||||||
} else {
|
|
||||||
BUG_ON(!ceph_dentry(dn));
|
|
||||||
|
|
||||||
dout("dn %p attached to %p ino %llx.%llx\n",
|
|
||||||
dn, dn->d_inode, ceph_vinop(dn->d_inode));
|
|
||||||
}
|
|
||||||
if ((!prehash || *prehash) && d_unhashed(dn))
|
|
||||||
d_rehash(dn);
|
|
||||||
out:
|
|
||||||
return dn;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set dentry's directory position based on the current dir's max, and
|
* Set dentry's directory position based on the current dir's max, and
|
||||||
* order it in d_subdirs, so that dcache_readdir behaves.
|
* order it in d_subdirs, so that dcache_readdir behaves.
|
||||||
@ -878,6 +834,52 @@ static void ceph_set_dentry_offset(struct dentry *dn)
|
|||||||
spin_unlock(&dcache_lock);
|
spin_unlock(&dcache_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* splice a dentry to an inode.
|
||||||
|
* caller must hold directory i_mutex for this to be safe.
|
||||||
|
*
|
||||||
|
* we will only rehash the resulting dentry if @prehash is
|
||||||
|
* true; @prehash will be set to false (for the benefit of
|
||||||
|
* the caller) if we fail.
|
||||||
|
*/
|
||||||
|
static struct dentry *splice_dentry(struct dentry *dn, struct inode *in,
|
||||||
|
bool *prehash)
|
||||||
|
{
|
||||||
|
struct dentry *realdn;
|
||||||
|
|
||||||
|
BUG_ON(dn->d_inode);
|
||||||
|
|
||||||
|
/* dn must be unhashed */
|
||||||
|
if (!d_unhashed(dn))
|
||||||
|
d_drop(dn);
|
||||||
|
realdn = d_materialise_unique(dn, in);
|
||||||
|
if (IS_ERR(realdn)) {
|
||||||
|
pr_err("splice_dentry error %p inode %p ino %llx.%llx\n",
|
||||||
|
dn, in, ceph_vinop(in));
|
||||||
|
if (prehash)
|
||||||
|
*prehash = false; /* don't rehash on error */
|
||||||
|
dn = realdn; /* note realdn contains the error */
|
||||||
|
goto out;
|
||||||
|
} else if (realdn) {
|
||||||
|
dout("dn %p (%d) spliced with %p (%d) "
|
||||||
|
"inode %p ino %llx.%llx\n",
|
||||||
|
dn, atomic_read(&dn->d_count),
|
||||||
|
realdn, atomic_read(&realdn->d_count),
|
||||||
|
realdn->d_inode, ceph_vinop(realdn->d_inode));
|
||||||
|
dput(dn);
|
||||||
|
dn = realdn;
|
||||||
|
} else {
|
||||||
|
BUG_ON(!ceph_dentry(dn));
|
||||||
|
dout("dn %p attached to %p ino %llx.%llx\n",
|
||||||
|
dn, dn->d_inode, ceph_vinop(dn->d_inode));
|
||||||
|
}
|
||||||
|
if ((!prehash || *prehash) && d_unhashed(dn))
|
||||||
|
d_rehash(dn);
|
||||||
|
ceph_set_dentry_offset(dn);
|
||||||
|
out:
|
||||||
|
return dn;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Incorporate results into the local cache. This is either just
|
* Incorporate results into the local cache. This is either just
|
||||||
* one inode, or a directory, dentry, and possibly linked-to inode (e.g.,
|
* one inode, or a directory, dentry, and possibly linked-to inode (e.g.,
|
||||||
@ -1030,6 +1032,9 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req,
|
|||||||
ceph_invalidate_dentry_lease(dn);
|
ceph_invalidate_dentry_lease(dn);
|
||||||
|
|
||||||
/* take overwritten dentry's readdir offset */
|
/* take overwritten dentry's readdir offset */
|
||||||
|
dout("dn %p gets %p offset %lld (old offset %lld)\n",
|
||||||
|
req->r_old_dentry, dn, ceph_dentry(dn)->offset,
|
||||||
|
ceph_dentry(req->r_old_dentry)->offset);
|
||||||
ceph_dentry(req->r_old_dentry)->offset =
|
ceph_dentry(req->r_old_dentry)->offset =
|
||||||
ceph_dentry(dn)->offset;
|
ceph_dentry(dn)->offset;
|
||||||
|
|
||||||
@ -1074,7 +1079,6 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req,
|
|||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
req->r_dentry = dn; /* may have spliced */
|
req->r_dentry = dn; /* may have spliced */
|
||||||
ceph_set_dentry_offset(dn);
|
|
||||||
igrab(in);
|
igrab(in);
|
||||||
} else if (ceph_ino(in) == vino.ino &&
|
} else if (ceph_ino(in) == vino.ino &&
|
||||||
ceph_snap(in) == vino.snap) {
|
ceph_snap(in) == vino.snap) {
|
||||||
@ -1117,7 +1121,6 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req,
|
|||||||
err = PTR_ERR(dn);
|
err = PTR_ERR(dn);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
ceph_set_dentry_offset(dn);
|
|
||||||
req->r_dentry = dn; /* may have spliced */
|
req->r_dentry = dn; /* may have spliced */
|
||||||
igrab(in);
|
igrab(in);
|
||||||
rinfo->head->is_dentry = 1; /* fool notrace handlers */
|
rinfo->head->is_dentry = 1; /* fool notrace handlers */
|
||||||
|
Loading…
Reference in New Issue
Block a user