@@ -36,6 +36,7 @@ static int jffs2_rmdir (struct inode *,struct dentry *);
static int jffs2_mknod (struct inode *,struct dentry *,umode_t,dev_t);
static int jffs2_rename (struct inode *, struct dentry *,
struct inode *, struct dentry *);
+static void jffs2_prescan(struct inode *dir_i, struct qstr *d_name);
const struct file_operations jffs2_dir_operations =
{
@@ -51,6 +52,7 @@ const struct inode_operations jffs2_dir_inode_operations =
{
.create = jffs2_create,
.lookup = jffs2_lookup,
+ .prescan = jffs2_prescan,
.link = jffs2_link,
.unlink = jffs2_unlink,
.symlink = jffs2_symlink,
@@ -74,8 +76,12 @@ const struct inode_operations jffs2_dir_inode_operations =
and we use the same hash function as the dentries. Makes this
nice and simple
*/
-static struct dentry *jffs2_lookup(struct inode *dir_i, struct dentry *target,
- unsigned int flags)
+/* The prescan function does not have a dentry to fill in, so create this common function
+ * which is just passed the name and the inode for the directory.
+ * This function is very similar to the original jffs2_lookup, except for the arguments
+ * and the fact that the dentry (now not passed) is not updated.
+ */
+static struct inode *jffs2_lookup_common(struct inode *dir_i, struct qstr *d_name)
{
struct jffs2_inode_info *dir_f;
struct jffs2_full_dirent *fd = NULL, *fd_list;
@@ -84,7 +90,7 @@ static struct dentry *jffs2_lookup(struct inode *dir_i, struct dentry *target,
jffs2_dbg(1, "jffs2_lookup()\n");
- if (target->d_name.len > JFFS2_MAX_NAME_LEN)
+ if (d_name->len > JFFS2_MAX_NAME_LEN)
return ERR_PTR(-ENAMETOOLONG);
dir_f = JFFS2_INODE_INFO(dir_i);
@@ -92,11 +98,11 @@ static struct dentry *jffs2_lookup(struct inode *dir_i, struct dentry *target,
mutex_lock(&dir_f->sem);
/* NB: The 2.2 backport will need to explicitly check for '.' and '..' here */
- for (fd_list = dir_f->dents; fd_list && fd_list->nhash <= target->d_name.hash; fd_list = fd_list->next) {
- if (fd_list->nhash == target->d_name.hash &&
+ for (fd_list = dir_f->dents; fd_list && fd_list->nhash <= d_name->hash; fd_list = fd_list->next) {
+ if (fd_list->nhash == d_name->hash &&
(!fd || fd_list->version > fd->version) &&
- strlen(fd_list->name) == target->d_name.len &&
- !strncmp(fd_list->name, target->d_name.name, target->d_name.len)) {
+ strlen(fd_list->name) == d_name->len &&
+ !strncmp(fd_list->name, d_name->name, d_name->len)) {
fd = fd_list;
}
}
@@ -108,6 +114,27 @@ static struct dentry *jffs2_lookup(struct inode *dir_i, struct dentry *target,
if (IS_ERR(inode))
pr_warn("iget() failed for ino #%u\n", ino);
}
+ return inode;
+}
+
+/* Fill in an inode, and store the information in cache. This allows a
+ * subsequent jffs2_lookup() call to proceed quickly, which is useful
+ * since the jffs2_lookup() call will have the directory entry cache
+ * locked.
+ */
+static void jffs2_prescan(struct inode *dir_i, struct qstr *d_name)
+{
+ (void)jffs2_lookup_common(dir_i, d_name);
+}
+
+/* jffs2_lookup function has the same functionality as before,
+ * just refactored */
+static struct dentry *jffs2_lookup(struct inode *dir_i, struct dentry *target,
+ unsigned int flags)
+{
+ struct inode *inode;
+
+ inode = jffs2_lookup_common(dir_i, &target->d_name);
return d_splice_alias(inode, target);
}
@@ -1322,13 +1322,12 @@ static void follow_dotdot(struct nameidata *nd)
*
* dir->d_inode->i_mutex must be held
*/
-static struct dentry *lookup_dcache(struct qstr *name, struct dentry *dir,
- unsigned int flags, bool *need_lookup)
+static struct dentry *lookup_dcache_no_alloc(struct qstr *name, struct dentry *dir,
+ unsigned int flags)
{
struct dentry *dentry;
int error;
- *need_lookup = false;
dentry = d_lookup(dir, name);
if (dentry) {
if (dentry->d_flags & DCACHE_OP_REVALIDATE) {
@@ -1345,6 +1344,16 @@ static struct dentry *lookup_dcache(struct qstr *name, struct dentry *dir,
}
}
}
+ return dentry;
+}
+
+static struct dentry *lookup_dcache(struct qstr *name, struct dentry *dir,
+ unsigned int flags, bool *need_lookup)
+{
+ struct dentry *dentry;
+
+ *need_lookup = false;
+ dentry = lookup_dcache_no_alloc(name, dir, flags);
if (!dentry) {
dentry = d_alloc(dir, name);
@@ -1497,9 +1506,32 @@ static int lookup_slow(struct nameidata *nd, struct path *path)
parent = nd->path.dentry;
BUG_ON(nd->inode != parent->d_inode);
- mutex_lock(&parent->d_inode->i_mutex);
- dentry = __lookup_hash(&nd->last, parent, nd->flags);
- mutex_unlock(&parent->d_inode->i_mutex);
+ dentry = NULL;
+ if (parent->d_inode->i_op->prescan) {
+ /* First, just try to find the dentry in the cache.
+ * If it is not present, don't do anything.
+ */
+ mutex_lock(&parent->d_inode->i_mutex);
+ dentry = lookup_dcache_no_alloc(&nd->last, parent, nd->flags);
+ mutex_unlock(&parent->d_inode->i_mutex);
+
+ /* Not in cache. Warn filesystem layer that a lookup is coming.
+ * This can be done without the directory's mutex held, since
+ * no dentry is being filled in here.
+ */
+ if (dentry == NULL) {
+ parent->d_inode->i_op->prescan(parent->d_inode, &nd->last);
+ }
+ }
+ if (dentry == NULL) {
+ /* Actually perform the lookup via the filesystem. The prescan
+ * done above will hopefully ensure this is now a quick operation,
+ * so the directory's mutex is not held for a long time.
+ */
+ mutex_lock(&parent->d_inode->i_mutex);
+ dentry = __lookup_hash(&nd->last, parent, nd->flags);
+ mutex_unlock(&parent->d_inode->i_mutex);
+ }
if (IS_ERR(dentry))
return PTR_ERR(dentry);
path->mnt = nd->path.mnt;
@@ -1639,6 +1639,7 @@ struct inode_operations {
umode_t create_mode, int *opened);
int (*tmpfile) (struct inode *, struct dentry *, umode_t);
int (*set_acl)(struct inode *, struct posix_acl *, int);
+ void (*prescan) (struct inode *dir, struct qstr *name);
/* WARNING: probably going away soon, do not use! */
int (*dentry_open)(struct dentry *, struct file *, const struct cred *);