[RFC PATCH v2] ovl: suppress negative dentry in lookup

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



When a file is only in a lower layer or in no layer at all, after
lookup a negative dentry will be generated in the upper layer or
even worse many negetive dentries will be generated in upper/lower
layers. These negative dentries will be useless after construction
of overlayfs' own dentry and may keep in the memory long time even
after unmount of overlayfs instance. This patch tries to kill
unnecessary negative dentry during lookup.

Signed-off-by: Chengguang Xu <cgxu519@xxxxxxxxxxxx>
---
v1->v2:
- Only drop negative dentry after slow lookup.

 fs/namei.c            |  9 ++++++---
 fs/overlayfs/namei.c  | 35 ++++++++++++++++++++++++++++++++++-
 include/linux/namei.h |  8 ++++++++
 3 files changed, 48 insertions(+), 4 deletions(-)

diff --git a/fs/namei.c b/fs/namei.c
index a320371899cf..1cc2960c7804 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -1386,7 +1386,7 @@ static inline int handle_mounts(struct nameidata *nd, struct dentry *dentry,
  * This looks up the name in dcache and possibly revalidates the found dentry.
  * NULL is returned if the dentry does not exist in the cache.
  */
-static struct dentry *lookup_dcache(const struct qstr *name,
+struct dentry *lookup_dcache(const struct qstr *name,
 				    struct dentry *dir,
 				    unsigned int flags)
 {
@@ -1402,6 +1402,7 @@ static struct dentry *lookup_dcache(const struct qstr *name,
 	}
 	return dentry;
 }
+EXPORT_SYMBOL(lookup_dcache);
 
 /*
  * Parent directory has inode locked exclusive.  This is one
@@ -1500,7 +1501,7 @@ static struct dentry *lookup_fast(struct nameidata *nd,
 }
 
 /* Fast lookup failed, do it the slow way */
-static struct dentry *__lookup_slow(const struct qstr *name,
+struct dentry *__lookup_slow(const struct qstr *name,
 				    struct dentry *dir,
 				    unsigned int flags)
 {
@@ -1536,6 +1537,7 @@ static struct dentry *__lookup_slow(const struct qstr *name,
 	}
 	return dentry;
 }
+EXPORT_SYMBOL(__lookup_slow);
 
 static struct dentry *lookup_slow(const struct qstr *name,
 				  struct dentry *dir,
@@ -2460,7 +2462,7 @@ int vfs_path_lookup(struct dentry *dentry, struct vfsmount *mnt,
 }
 EXPORT_SYMBOL(vfs_path_lookup);
 
-static int lookup_one_len_common(const char *name, struct dentry *base,
+int lookup_one_len_common(const char *name, struct dentry *base,
 				 int len, struct qstr *this)
 {
 	this->name = name;
@@ -2491,6 +2493,7 @@ static int lookup_one_len_common(const char *name, struct dentry *base,
 
 	return inode_permission(base->d_inode, MAY_EXEC);
 }
+EXPORT_SYMBOL(lookup_one_len_common);
 
 /**
  * try_lookup_one_len - filesystem helper to lookup single pathname component
diff --git a/fs/overlayfs/namei.c b/fs/overlayfs/namei.c
index 723d17744758..d8e71173ea75 100644
--- a/fs/overlayfs/namei.c
+++ b/fs/overlayfs/namei.c
@@ -191,6 +191,39 @@ static bool ovl_is_opaquedir(struct dentry *dentry)
 	return ovl_check_dir_xattr(dentry, OVL_XATTR_OPAQUE);
 }
 
+static struct dentry *ovl_lookup_positive_unlocked(const char *name,
+						   struct dentry *base,
+						   int len)
+{
+	struct qstr this;
+	struct dentry *ret;
+	bool not_found = false;
+	int err;
+
+	err = lookup_one_len_common(name, base, len, &this);
+	if (err)
+		return ERR_PTR(err);
+
+	ret = lookup_dcache(&this, base, 0);
+	if (ret)
+		return ret;
+
+	inode_lock_shared(base->d_inode);
+	ret = __lookup_slow(&this, base, 0);
+	if (!IS_ERR(ret) &&
+	    d_flags_negative(ret->d_flags)) {
+		not_found = true;
+		d_drop(ret);
+	}
+	inode_unlock_shared(base->d_inode);
+
+	if (not_found) {
+		dput(ret);
+		ret = ERR_PTR(-ENOENT);
+	}
+	return ret;
+}
+
 static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d,
 			     const char *name, unsigned int namelen,
 			     size_t prelen, const char *post,
@@ -200,7 +233,7 @@ static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d,
 	int err;
 	bool last_element = !post[0];
 
-	this = lookup_positive_unlocked(name, base, namelen);
+	this = ovl_lookup_positive_unlocked(name, base, namelen);
 	if (IS_ERR(this)) {
 		err = PTR_ERR(this);
 		this = NULL;
diff --git a/include/linux/namei.h b/include/linux/namei.h
index a4bb992623c4..c65b863657eb 100644
--- a/include/linux/namei.h
+++ b/include/linux/namei.h
@@ -66,6 +66,14 @@ extern struct dentry *user_path_create(int, const char __user *, struct path *,
 extern void done_path_create(struct path *, struct dentry *);
 extern struct dentry *kern_path_locked(const char *, struct path *);
 
+extern int lookup_one_len_common(const char *name, struct dentry *base,
+				 int len, struct qstr *this);
+extern struct dentry *lookup_dcache(const struct qstr *name,
+				    struct dentry *base,
+				    unsigned int flags);
+extern struct dentry *__lookup_slow(const struct qstr *name,
+				    struct dentry *dir,
+				    unsigned int flags);
 extern struct dentry *try_lookup_one_len(const char *, struct dentry *, int);
 extern struct dentry *lookup_one_len(const char *, struct dentry *, int);
 extern struct dentry *lookup_one_len_unlocked(const char *, struct dentry *, int);
-- 
2.20.1






[Index of Archives]     [Linux Filesystems Devel]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux