|
@@ -40,14 +40,6 @@ int ceph_init_dentry(struct dentry *dentry)
|
|
if (dentry->d_fsdata)
|
|
if (dentry->d_fsdata)
|
|
return 0;
|
|
return 0;
|
|
|
|
|
|
- if (dentry->d_parent == NULL || /* nfs fh_to_dentry */
|
|
|
|
- ceph_snap(dentry->d_parent->d_inode) == CEPH_NOSNAP)
|
|
|
|
- d_set_d_op(dentry, &ceph_dentry_ops);
|
|
|
|
- else if (ceph_snap(dentry->d_parent->d_inode) == CEPH_SNAPDIR)
|
|
|
|
- d_set_d_op(dentry, &ceph_snapdir_dentry_ops);
|
|
|
|
- else
|
|
|
|
- d_set_d_op(dentry, &ceph_snap_dentry_ops);
|
|
|
|
-
|
|
|
|
di = kmem_cache_alloc(ceph_dentry_cachep, GFP_NOFS | __GFP_ZERO);
|
|
di = kmem_cache_alloc(ceph_dentry_cachep, GFP_NOFS | __GFP_ZERO);
|
|
if (!di)
|
|
if (!di)
|
|
return -ENOMEM; /* oh well */
|
|
return -ENOMEM; /* oh well */
|
|
@@ -58,16 +50,42 @@ int ceph_init_dentry(struct dentry *dentry)
|
|
kmem_cache_free(ceph_dentry_cachep, di);
|
|
kmem_cache_free(ceph_dentry_cachep, di);
|
|
goto out_unlock;
|
|
goto out_unlock;
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ if (dentry->d_parent == NULL || /* nfs fh_to_dentry */
|
|
|
|
+ ceph_snap(dentry->d_parent->d_inode) == CEPH_NOSNAP)
|
|
|
|
+ d_set_d_op(dentry, &ceph_dentry_ops);
|
|
|
|
+ else if (ceph_snap(dentry->d_parent->d_inode) == CEPH_SNAPDIR)
|
|
|
|
+ d_set_d_op(dentry, &ceph_snapdir_dentry_ops);
|
|
|
|
+ else
|
|
|
|
+ d_set_d_op(dentry, &ceph_snap_dentry_ops);
|
|
|
|
+
|
|
di->dentry = dentry;
|
|
di->dentry = dentry;
|
|
di->lease_session = NULL;
|
|
di->lease_session = NULL;
|
|
- dentry->d_fsdata = di;
|
|
|
|
dentry->d_time = jiffies;
|
|
dentry->d_time = jiffies;
|
|
|
|
+ /* avoid reordering d_fsdata setup so that the check above is safe */
|
|
|
|
+ smp_mb();
|
|
|
|
+ dentry->d_fsdata = di;
|
|
ceph_dentry_lru_add(dentry);
|
|
ceph_dentry_lru_add(dentry);
|
|
out_unlock:
|
|
out_unlock:
|
|
spin_unlock(&dentry->d_lock);
|
|
spin_unlock(&dentry->d_lock);
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+struct inode *ceph_get_dentry_parent_inode(struct dentry *dentry)
|
|
|
|
+{
|
|
|
|
+ struct inode *inode = NULL;
|
|
|
|
+
|
|
|
|
+ if (!dentry)
|
|
|
|
+ return NULL;
|
|
|
|
+
|
|
|
|
+ spin_lock(&dentry->d_lock);
|
|
|
|
+ if (dentry->d_parent) {
|
|
|
|
+ inode = dentry->d_parent->d_inode;
|
|
|
|
+ ihold(inode);
|
|
|
|
+ }
|
|
|
|
+ spin_unlock(&dentry->d_lock);
|
|
|
|
+ return inode;
|
|
|
|
+}
|
|
|
|
|
|
|
|
|
|
/*
|
|
/*
|
|
@@ -133,7 +151,7 @@ more:
|
|
d_unhashed(dentry) ? "!hashed" : "hashed",
|
|
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->flags |= CEPH_F_ATEND;
|
|
goto out_unlock;
|
|
goto out_unlock;
|
|
}
|
|
}
|
|
spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
|
|
spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
|
|
@@ -234,7 +252,7 @@ static int ceph_readdir(struct file *filp, void *dirent, filldir_t filldir)
|
|
const int max_bytes = fsc->mount_options->max_readdir_bytes;
|
|
const int max_bytes = fsc->mount_options->max_readdir_bytes;
|
|
|
|
|
|
dout("readdir %p filp %p frag %u off %u\n", inode, filp, frag, off);
|
|
dout("readdir %p filp %p frag %u off %u\n", inode, filp, frag, off);
|
|
- if (fi->at_end)
|
|
|
|
|
|
+ if (fi->flags & CEPH_F_ATEND)
|
|
return 0;
|
|
return 0;
|
|
|
|
|
|
/* always start with . and .. */
|
|
/* always start with . and .. */
|
|
@@ -403,7 +421,7 @@ more:
|
|
dout("readdir next frag is %x\n", frag);
|
|
dout("readdir next frag is %x\n", frag);
|
|
goto more;
|
|
goto more;
|
|
}
|
|
}
|
|
- fi->at_end = 1;
|
|
|
|
|
|
+ fi->flags |= CEPH_F_ATEND;
|
|
|
|
|
|
/*
|
|
/*
|
|
* if dir_release_count still matches the dir, no dentries
|
|
* if dir_release_count still matches the dir, no dentries
|
|
@@ -435,7 +453,7 @@ static void reset_readdir(struct ceph_file_info *fi)
|
|
dput(fi->dentry);
|
|
dput(fi->dentry);
|
|
fi->dentry = NULL;
|
|
fi->dentry = NULL;
|
|
}
|
|
}
|
|
- fi->at_end = 0;
|
|
|
|
|
|
+ fi->flags &= ~CEPH_F_ATEND;
|
|
}
|
|
}
|
|
|
|
|
|
static loff_t ceph_dir_llseek(struct file *file, loff_t offset, int origin)
|
|
static loff_t ceph_dir_llseek(struct file *file, loff_t offset, int origin)
|
|
@@ -463,7 +481,7 @@ static loff_t ceph_dir_llseek(struct file *file, loff_t offset, int origin)
|
|
if (offset != file->f_pos) {
|
|
if (offset != file->f_pos) {
|
|
file->f_pos = offset;
|
|
file->f_pos = offset;
|
|
file->f_version = 0;
|
|
file->f_version = 0;
|
|
- fi->at_end = 0;
|
|
|
|
|
|
+ fi->flags &= ~CEPH_F_ATEND;
|
|
}
|
|
}
|
|
retval = offset;
|
|
retval = offset;
|
|
|
|
|
|
@@ -488,21 +506,13 @@ out:
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
/*
|
|
- * Process result of a lookup/open request.
|
|
|
|
- *
|
|
|
|
- * Mainly, make sure we return the final req->r_dentry (if it already
|
|
|
|
- * existed) in place of the original VFS-provided dentry when they
|
|
|
|
- * differ.
|
|
|
|
- *
|
|
|
|
- * Gracefully handle the case where the MDS replies with -ENOENT and
|
|
|
|
- * no trace (which it may do, at its discretion, e.g., if it doesn't
|
|
|
|
- * care to issue a lease on the negative dentry).
|
|
|
|
|
|
+ * Handle lookups for the hidden .snap directory.
|
|
*/
|
|
*/
|
|
-struct dentry *ceph_finish_lookup(struct ceph_mds_request *req,
|
|
|
|
- struct dentry *dentry, int err)
|
|
|
|
|
|
+int ceph_handle_snapdir(struct ceph_mds_request *req,
|
|
|
|
+ struct dentry *dentry, int err)
|
|
{
|
|
{
|
|
struct ceph_fs_client *fsc = ceph_sb_to_client(dentry->d_sb);
|
|
struct ceph_fs_client *fsc = ceph_sb_to_client(dentry->d_sb);
|
|
- struct inode *parent = dentry->d_parent->d_inode;
|
|
|
|
|
|
+ struct inode *parent = dentry->d_parent->d_inode; /* we hold i_mutex */
|
|
|
|
|
|
/* .snap dir? */
|
|
/* .snap dir? */
|
|
if (err == -ENOENT &&
|
|
if (err == -ENOENT &&
|
|
@@ -516,7 +526,23 @@ struct dentry *ceph_finish_lookup(struct ceph_mds_request *req,
|
|
d_add(dentry, inode);
|
|
d_add(dentry, inode);
|
|
err = 0;
|
|
err = 0;
|
|
}
|
|
}
|
|
|
|
+ return err;
|
|
|
|
+}
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * Figure out final result of a lookup/open request.
|
|
|
|
+ *
|
|
|
|
+ * Mainly, make sure we return the final req->r_dentry (if it already
|
|
|
|
+ * existed) in place of the original VFS-provided dentry when they
|
|
|
|
+ * differ.
|
|
|
|
+ *
|
|
|
|
+ * Gracefully handle the case where the MDS replies with -ENOENT and
|
|
|
|
+ * no trace (which it may do, at its discretion, e.g., if it doesn't
|
|
|
|
+ * care to issue a lease on the negative dentry).
|
|
|
|
+ */
|
|
|
|
+struct dentry *ceph_finish_lookup(struct ceph_mds_request *req,
|
|
|
|
+ struct dentry *dentry, int err)
|
|
|
|
+{
|
|
if (err == -ENOENT) {
|
|
if (err == -ENOENT) {
|
|
/* no trace? */
|
|
/* no trace? */
|
|
err = 0;
|
|
err = 0;
|
|
@@ -610,6 +636,7 @@ static struct dentry *ceph_lookup(struct inode *dir, struct dentry *dentry,
|
|
req->r_args.getattr.mask = cpu_to_le32(CEPH_STAT_CAP_INODE);
|
|
req->r_args.getattr.mask = cpu_to_le32(CEPH_STAT_CAP_INODE);
|
|
req->r_locked_dir = dir;
|
|
req->r_locked_dir = dir;
|
|
err = ceph_mdsc_do_request(mdsc, NULL, req);
|
|
err = ceph_mdsc_do_request(mdsc, NULL, req);
|
|
|
|
+ err = ceph_handle_snapdir(req, dentry, err);
|
|
dentry = ceph_finish_lookup(req, dentry, err);
|
|
dentry = ceph_finish_lookup(req, dentry, err);
|
|
ceph_mdsc_put_request(req); /* will dput(dentry) */
|
|
ceph_mdsc_put_request(req); /* will dput(dentry) */
|
|
dout("lookup result=%p\n", dentry);
|
|
dout("lookup result=%p\n", dentry);
|
|
@@ -789,6 +816,7 @@ static int ceph_link(struct dentry *old_dentry, struct inode *dir,
|
|
req->r_dentry = dget(dentry);
|
|
req->r_dentry = dget(dentry);
|
|
req->r_num_caps = 2;
|
|
req->r_num_caps = 2;
|
|
req->r_old_dentry = dget(old_dentry); /* or inode? hrm. */
|
|
req->r_old_dentry = dget(old_dentry); /* or inode? hrm. */
|
|
|
|
+ req->r_old_dentry_dir = ceph_get_dentry_parent_inode(old_dentry);
|
|
req->r_locked_dir = dir;
|
|
req->r_locked_dir = dir;
|
|
req->r_dentry_drop = CEPH_CAP_FILE_SHARED;
|
|
req->r_dentry_drop = CEPH_CAP_FILE_SHARED;
|
|
req->r_dentry_unless = CEPH_CAP_FILE_EXCL;
|
|
req->r_dentry_unless = CEPH_CAP_FILE_EXCL;
|
|
@@ -887,6 +915,7 @@ static int ceph_rename(struct inode *old_dir, struct dentry *old_dentry,
|
|
req->r_dentry = dget(new_dentry);
|
|
req->r_dentry = dget(new_dentry);
|
|
req->r_num_caps = 2;
|
|
req->r_num_caps = 2;
|
|
req->r_old_dentry = dget(old_dentry);
|
|
req->r_old_dentry = dget(old_dentry);
|
|
|
|
+ req->r_old_dentry_dir = ceph_get_dentry_parent_inode(old_dentry);
|
|
req->r_locked_dir = new_dir;
|
|
req->r_locked_dir = new_dir;
|
|
req->r_old_dentry_drop = CEPH_CAP_FILE_SHARED;
|
|
req->r_old_dentry_drop = CEPH_CAP_FILE_SHARED;
|
|
req->r_old_dentry_unless = CEPH_CAP_FILE_EXCL;
|
|
req->r_old_dentry_unless = CEPH_CAP_FILE_EXCL;
|
|
@@ -1002,36 +1031,38 @@ static int dir_lease_is_valid(struct inode *dir, struct dentry *dentry)
|
|
*/
|
|
*/
|
|
static int ceph_d_revalidate(struct dentry *dentry, struct nameidata *nd)
|
|
static int ceph_d_revalidate(struct dentry *dentry, struct nameidata *nd)
|
|
{
|
|
{
|
|
|
|
+ int valid = 0;
|
|
struct inode *dir;
|
|
struct inode *dir;
|
|
|
|
|
|
if (nd && nd->flags & LOOKUP_RCU)
|
|
if (nd && nd->flags & LOOKUP_RCU)
|
|
return -ECHILD;
|
|
return -ECHILD;
|
|
|
|
|
|
- dir = dentry->d_parent->d_inode;
|
|
|
|
-
|
|
|
|
dout("d_revalidate %p '%.*s' inode %p offset %lld\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);
|
|
ceph_dentry(dentry)->offset);
|
|
|
|
|
|
|
|
+ dir = ceph_get_dentry_parent_inode(dentry);
|
|
|
|
+
|
|
/* 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) {
|
|
dout("d_revalidate %p '%.*s' inode %p is SNAPPED\n", dentry,
|
|
dout("d_revalidate %p '%.*s' inode %p is SNAPPED\n", dentry,
|
|
dentry->d_name.len, dentry->d_name.name, dentry->d_inode);
|
|
dentry->d_name.len, dentry->d_name.name, dentry->d_inode);
|
|
- goto out_touch;
|
|
|
|
|
|
+ valid = 1;
|
|
|
|
+ } else if (dentry->d_inode &&
|
|
|
|
+ ceph_snap(dentry->d_inode) == CEPH_SNAPDIR) {
|
|
|
|
+ valid = 1;
|
|
|
|
+ } else if (dentry_lease_is_valid(dentry) ||
|
|
|
|
+ dir_lease_is_valid(dir, dentry)) {
|
|
|
|
+ valid = 1;
|
|
}
|
|
}
|
|
- if (dentry->d_inode && ceph_snap(dentry->d_inode) == CEPH_SNAPDIR)
|
|
|
|
- goto out_touch;
|
|
|
|
-
|
|
|
|
- if (dentry_lease_is_valid(dentry) ||
|
|
|
|
- dir_lease_is_valid(dir, dentry))
|
|
|
|
- goto out_touch;
|
|
|
|
|
|
|
|
- dout("d_revalidate %p invalid\n", dentry);
|
|
|
|
- d_drop(dentry);
|
|
|
|
- return 0;
|
|
|
|
-out_touch:
|
|
|
|
- ceph_dentry_lru_touch(dentry);
|
|
|
|
- return 1;
|
|
|
|
|
|
+ dout("d_revalidate %p %s\n", dentry, valid ? "valid" : "invalid");
|
|
|
|
+ if (valid)
|
|
|
|
+ ceph_dentry_lru_touch(dentry);
|
|
|
|
+ else
|
|
|
|
+ d_drop(dentry);
|
|
|
|
+ iput(dir);
|
|
|
|
+ return valid;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
/*
|
|
@@ -1228,9 +1259,8 @@ void ceph_dentry_lru_del(struct dentry *dn)
|
|
* Return name hash for a given dentry. This is dependent on
|
|
* Return name hash for a given dentry. This is dependent on
|
|
* the parent directory's hash function.
|
|
* the parent directory's hash function.
|
|
*/
|
|
*/
|
|
-unsigned ceph_dentry_hash(struct dentry *dn)
|
|
|
|
|
|
+unsigned ceph_dentry_hash(struct inode *dir, struct dentry *dn)
|
|
{
|
|
{
|
|
- struct inode *dir = dn->d_parent->d_inode;
|
|
|
|
struct ceph_inode_info *dci = ceph_inode(dir);
|
|
struct ceph_inode_info *dci = ceph_inode(dir);
|
|
|
|
|
|
switch (dci->i_dir_layout.dl_dir_hash) {
|
|
switch (dci->i_dir_layout.dl_dir_hash) {
|