linux-fsdevel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v7 0/6] fs/dcache: Track & limit # of negative dentries
@ 2018-07-12 16:45 Waiman Long
  2018-07-12 16:46 ` [PATCH v7 1/6] fs/dcache: Track & report number " Waiman Long
                   ` (5 more replies)
  0 siblings, 6 replies; 11+ messages in thread
From: Waiman Long @ 2018-07-12 16:45 UTC (permalink / raw)
  To: Alexander Viro, Jonathan Corbet, Luis R. Rodriguez, Kees Cook
  Cc: linux-kernel, linux-fsdevel, linux-mm, linux-doc, Linus Torvalds,
	Jan Kara, Paul E. McKenney, Andrew Morton, Ingo Molnar,
	Miklos Szeredi, Matthew Wilcox, Larry Woodman, James Bottomley,
	Wangkai (Kevin C),
	Michal Hocko, Waiman Long

 v6->v7:
  - Drop the 2 patches that add a new shrinker for negative dentries.
    Instead, the default memory shrinker will be relied on to get
    rid of excess negative dentries when free memory is low.
  - Change the sysctl parameter "neg-dentry-pc" to "neg-dentry-limit"
    and the unit is now 1/1000 of the total system memory with a range
    of 0-100.
  - Add a patch to brief up the warning so that one warning will be
    printed every minute to the console if the limit is exceeded until
    the system administrator does something about it. It can be disabling
    the limit, raising the limit or turning on the enforcement option.

 v5->v6:
  - Drop the neg_dentry_pc boot command line option, but add a
    "neg-dentry-pc" sysctl parameter instead.
  - Change the "enforce-neg-dentry-limit" sysctl parameter to
    "neg-dentry-enforce".
  - Add a patch to add negative dentry to the head of the LRU initially
    so that they will be the first to be removed if they are not
    accessed again.
  - Run some additional performance test.

 v4->v5:
  - Backed to the latest 4.18 kernel and modify the code
    accordingly. Patch 1 "Relocate dentry_kill() after lock_parent()"
    is now no longer necessary.
  - Make tracking and limiting of negative dentries a user configurable
    option (CONFIG_DCACHE_TRACK_NEG_ENTRY) so that users can decide if
    they want to include this capability in the kernel.
  - Make killing excess negative dentries an optional feature that can be
    enabled via a boot command line option or a sysctl parameter.
  - Spread negative dentry pruning across multiple CPUs.

 v4: https://lkml.org/lkml/2017/9/18/739
 v5: https://lkml.org/lkml/2018/7/2/21
 v6: https://lkml.org/lkml/2018/7/6/875

A rogue application can potentially create a large number of negative
dentries in the system consuming a significant portion of the memory
available if it is not under the direct control of a memory controller
that enforces kernel memory limit. Unlike other user activities that
can be easily tracked, generation of too many negative dentries is hard
to detect.

This patchset introduces changes to the dcache subsystem to track and
optionally limit the number of negative dentries allowed to be created.
Once the limit is exceeded, warnings will be printed in the console.
System administrators can then either disable the limit, raise the limit
or enforce the limit by killing off excess negative dentries. They
should also investigate if something wrong is happening that causes
the number of negative dentries used to go way up.

Patch 1 tracks the number of negative dentries present in the LRU
lists and reports it in /proc/sys/fs/dentry-state.

Patch 2 moves the negative dentries to the head of the LRU after they
are initially created. They will be moved to the tail like the positive
dentries the second time they are reinserted. This will make sure that
all those accssed-once negative dentries will be removed first when a
shrinker is running.

Patch 3 adds a "neg-dentry-limit" sysctl parameter that can be used to
to specify a soft limit on the number of negative allowed as a multiple
of 1/1000 of the total system memory available. This parameter is 0 by
default which means no negative dentry limiting will be performed.

Patch 4 briefs up the warning to once every minute and gives instruction
on how to disable it.

Patch 5 adds a "neg-dentry-enforce" sysctl parameter which can be
dynamically enabled at run time to enforce the negative dentry limit
by killing excess newly created negative dentires right after use,
if necessary.

Patch 6 makes the negative dentry limiting code a user configurable
option so that it can be configured out, if desired.

With a 4.18 based kernel and a simple microbenchmark to measure dentry
lookup and creation rates, the rates (average on 3 runs) after initial
boot on a 2-socket 24-core 48-thread 128G memory system with and without
the patch were as follows:

  Metric               w/o patch   limit=0  limit=1  limit=1,enforce
  ------               ---------   -------  -------  ---------------
  +ve dentry lookup      672313     679720   676250      676071
  -ve dentry lookup     1547694    1555210  1542731     1559395
  -ve dentry creation    696427     700975   697713      693494

For the lookup and creation rates, there wasn't any signifcant difference
with or without the patch. It could be hard to compare the figures as
they were from different kernels and other factors like code layout
could have an impact.

When the limit was enabled, a very slight performance drop (0.5%-0.8%)
was observed.

The negative dentry creation test created 10 millions unique negative
dentries. With a setting of neg-dentry-limit=1, the warning threshold
was about 339020.  So most of negative dentries were created with the
limit exceeded.  The performance drop was only about 0.5% which was not
much. Turning on the enforce option to kill excess negative dentries
dropped the performance by a further 0.6% (1.1% in total).

I had also run other microbenchmarks, but I didn't observe any changes
in performance that were not in the run-to-run variation range for
those benchmarks.

Waiman Long (6):
  fs/dcache: Track & report number of negative dentries
  fs/dcache: Add negative dentries to LRU head initially
  fs/dcache: Add sysctl parameter neg-dentry-limit as a soft limit on
    negative dentries
  fs/dcache: Print negative dentry warning every min until turned off by
    user
  fs/dcache: Allow optional enforcement of negative dentry limit
  fs/dcache: Allow deconfiguration of negative dentry code to reduce
    kernel size

 Documentation/sysctl/fs.txt |  40 +++++-
 fs/Kconfig                  |  10 ++
 fs/dcache.c                 | 310 +++++++++++++++++++++++++++++++++++++++++++-
 include/linux/dcache.h      |  17 ++-
 include/linux/list_lru.h    |  17 +++
 kernel/sysctl.c             |  22 ++++
 mm/list_lru.c               |  19 ++-
 7 files changed, 421 insertions(+), 14 deletions(-)

-- 
1.8.3.1

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH v7 1/6] fs/dcache: Track & report number of negative dentries
  2018-07-12 16:45 [PATCH v7 0/6] fs/dcache: Track & limit # of negative dentries Waiman Long
@ 2018-07-12 16:46 ` Waiman Long
  2018-07-12 16:46 ` [PATCH v7 2/6] fs/dcache: Add negative dentries to LRU head initially Waiman Long
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 11+ messages in thread
From: Waiman Long @ 2018-07-12 16:46 UTC (permalink / raw)
  To: Alexander Viro, Jonathan Corbet, Luis R. Rodriguez, Kees Cook
  Cc: linux-kernel, linux-fsdevel, linux-mm, linux-doc, Linus Torvalds,
	Jan Kara, Paul E. McKenney, Andrew Morton, Ingo Molnar,
	Miklos Szeredi, Matthew Wilcox, Larry Woodman, James Bottomley,
	Wangkai (Kevin C),
	Michal Hocko, Waiman Long

The current dentry number tracking code doesn't distinguish between
positive & negative dentries. It just reports the total number of
dentries in the LRU lists.

As excessive number of negative dentries can have an impact on system
performance, it will be wise to track the number of positive and
negative dentries separately.

This patch adds tracking for the total number of negative dentries in
the system LRU lists and reports it in the /proc/sys/fs/dentry-state
file.  The number of positive dentries in the LRU lists can be found
by subtracting the number of negative dentries from the total.

Signed-off-by: Waiman Long <longman@redhat.com>
---
 Documentation/sysctl/fs.txt | 19 +++++++++++++------
 fs/dcache.c                 | 45 +++++++++++++++++++++++++++++++++++++++++++++
 include/linux/dcache.h      |  7 ++++---
 3 files changed, 62 insertions(+), 9 deletions(-)

diff --git a/Documentation/sysctl/fs.txt b/Documentation/sysctl/fs.txt
index 6c00c1e..a8e3f1f 100644
--- a/Documentation/sysctl/fs.txt
+++ b/Documentation/sysctl/fs.txt
@@ -61,19 +61,26 @@ struct {
         int nr_unused;
         int age_limit;         /* age in seconds */
         int want_pages;        /* pages requested by system */
-        int dummy[2];
+        int nr_negative;       /* # of unused negative dentries */
+        int dummy;
 } dentry_stat = {0, 0, 45, 0,};
--------------------------------------------------------------- 
+--------------------------------------------------------------
+
+Dentries are dynamically allocated and deallocated.
+
+nr_dentry shows the total number of dentries allocated (active
++ unused). nr_unused shows the number of dentries that are not
+actively used, but are saved in the LRU list for future reuse.
 
-Dentries are dynamically allocated and deallocated, and
-nr_dentry seems to be 0 all the time. Hence it's safe to
-assume that only nr_unused, age_limit and want_pages are
-used. Nr_unused seems to be exactly what its name says.
 Age_limit is the age in seconds after which dcache entries
 can be reclaimed when memory is short and want_pages is
 nonzero when shrink_dcache_pages() has been called and the
 dcache isn't pruned yet.
 
+nr_negative shows the number of unused dentries that are also
+negative dentries which do not mapped to actual files if negative
+dentries tracking is enabled.
+
 ==============================================================
 
 dquot-max & dquot-nr:
diff --git a/fs/dcache.c b/fs/dcache.c
index 0e8e5de..dbab6c2 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -119,6 +119,7 @@ struct dentry_stat_t dentry_stat = {
 
 static DEFINE_PER_CPU(long, nr_dentry);
 static DEFINE_PER_CPU(long, nr_dentry_unused);
+static DEFINE_PER_CPU(long, nr_dentry_neg);
 
 #if defined(CONFIG_SYSCTL) && defined(CONFIG_PROC_FS)
 
@@ -152,11 +153,22 @@ static long get_nr_dentry_unused(void)
 	return sum < 0 ? 0 : sum;
 }
 
+static long get_nr_dentry_neg(void)
+{
+	int i;
+	long sum = 0;
+
+	for_each_possible_cpu(i)
+		sum += per_cpu(nr_dentry_neg, i);
+	return sum < 0 ? 0 : sum;
+}
+
 int proc_nr_dentry(struct ctl_table *table, int write, void __user *buffer,
 		   size_t *lenp, loff_t *ppos)
 {
 	dentry_stat.nr_dentry = get_nr_dentry();
 	dentry_stat.nr_unused = get_nr_dentry_unused();
+	dentry_stat.nr_negative = get_nr_dentry_neg();
 	return proc_doulongvec_minmax(table, write, buffer, lenp, ppos);
 }
 #endif
@@ -214,6 +226,28 @@ static inline int dentry_string_cmp(const unsigned char *cs, const unsigned char
 
 #endif
 
+static inline void __neg_dentry_dec(struct dentry *dentry)
+{
+	this_cpu_dec(nr_dentry_neg);
+}
+
+static inline void neg_dentry_dec(struct dentry *dentry)
+{
+	if (unlikely(d_is_negative(dentry)))
+		__neg_dentry_dec(dentry);
+}
+
+static inline void __neg_dentry_inc(struct dentry *dentry)
+{
+	this_cpu_inc(nr_dentry_neg);
+}
+
+static inline void neg_dentry_inc(struct dentry *dentry)
+{
+	if (unlikely(d_is_negative(dentry)))
+		__neg_dentry_inc(dentry);
+}
+
 static inline int dentry_cmp(const struct dentry *dentry, const unsigned char *ct, unsigned tcount)
 {
 	/*
@@ -330,6 +364,8 @@ static inline void __d_clear_type_and_inode(struct dentry *dentry)
 	flags &= ~(DCACHE_ENTRY_TYPE | DCACHE_FALLTHRU);
 	WRITE_ONCE(dentry->d_flags, flags);
 	dentry->d_inode = NULL;
+	if (dentry->d_flags & DCACHE_LRU_LIST)
+		__neg_dentry_inc(dentry);
 }
 
 static void dentry_free(struct dentry *dentry)
@@ -397,6 +433,7 @@ static void d_lru_add(struct dentry *dentry)
 	dentry->d_flags |= DCACHE_LRU_LIST;
 	this_cpu_inc(nr_dentry_unused);
 	WARN_ON_ONCE(!list_lru_add(&dentry->d_sb->s_dentry_lru, &dentry->d_lru));
+	neg_dentry_inc(dentry);
 }
 
 static void d_lru_del(struct dentry *dentry)
@@ -405,6 +442,7 @@ static void d_lru_del(struct dentry *dentry)
 	dentry->d_flags &= ~DCACHE_LRU_LIST;
 	this_cpu_dec(nr_dentry_unused);
 	WARN_ON_ONCE(!list_lru_del(&dentry->d_sb->s_dentry_lru, &dentry->d_lru));
+	neg_dentry_dec(dentry);
 }
 
 static void d_shrink_del(struct dentry *dentry)
@@ -435,6 +473,7 @@ static void d_lru_isolate(struct list_lru_one *lru, struct dentry *dentry)
 	dentry->d_flags &= ~DCACHE_LRU_LIST;
 	this_cpu_dec(nr_dentry_unused);
 	list_lru_isolate(lru, &dentry->d_lru);
+	neg_dentry_dec(dentry);
 }
 
 static void d_lru_shrink_move(struct list_lru_one *lru, struct dentry *dentry,
@@ -443,6 +482,7 @@ static void d_lru_shrink_move(struct list_lru_one *lru, struct dentry *dentry,
 	D_FLAG_VERIFY(dentry, DCACHE_LRU_LIST);
 	dentry->d_flags |= DCACHE_SHRINK_LIST;
 	list_lru_isolate_move(lru, &dentry->d_lru, list);
+	neg_dentry_dec(dentry);
 }
 
 /**
@@ -1842,6 +1882,11 @@ static void __d_instantiate(struct dentry *dentry, struct inode *inode)
 	WARN_ON(d_in_lookup(dentry));
 
 	spin_lock(&dentry->d_lock);
+	/*
+	 * Decrement negative dentry count if it was in the LRU list.
+	 */
+	if (dentry->d_flags & DCACHE_LRU_LIST)
+		__neg_dentry_dec(dentry);
 	hlist_add_head(&dentry->d_u.d_alias, &inode->i_dentry);
 	raw_write_seqcount_begin(&dentry->d_seq);
 	__d_set_inode_and_type(dentry, inode, add_flags);
diff --git a/include/linux/dcache.h b/include/linux/dcache.h
index 66c6e17..6e06d91 100644
--- a/include/linux/dcache.h
+++ b/include/linux/dcache.h
@@ -62,9 +62,10 @@ struct qstr {
 struct dentry_stat_t {
 	long nr_dentry;
 	long nr_unused;
-	long age_limit;          /* age in seconds */
-	long want_pages;         /* pages requested by system */
-	long dummy[2];
+	long age_limit;		/* age in seconds */
+	long want_pages;	/* pages requested by system */
+	long nr_negative;	/* # of unused negative dentries */
+	long dummy;
 };
 extern struct dentry_stat_t dentry_stat;
 
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v7 2/6] fs/dcache: Add negative dentries to LRU head initially
  2018-07-12 16:45 [PATCH v7 0/6] fs/dcache: Track & limit # of negative dentries Waiman Long
  2018-07-12 16:46 ` [PATCH v7 1/6] fs/dcache: Track & report number " Waiman Long
@ 2018-07-12 16:46 ` Waiman Long
  2018-07-12 16:46 ` [PATCH v7 3/6] fs/dcache: Add sysctl parameter neg-dentry-limit as a soft limit on negative dentries Waiman Long
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 11+ messages in thread
From: Waiman Long @ 2018-07-12 16:46 UTC (permalink / raw)
  To: Alexander Viro, Jonathan Corbet, Luis R. Rodriguez, Kees Cook
  Cc: linux-kernel, linux-fsdevel, linux-mm, linux-doc, Linus Torvalds,
	Jan Kara, Paul E. McKenney, Andrew Morton, Ingo Molnar,
	Miklos Szeredi, Matthew Wilcox, Larry Woodman, James Bottomley,
	Wangkai (Kevin C),
	Michal Hocko, Waiman Long

For negative dentries that are accessed once and never reused again,
there is not much value in putting the dentries at the tail of the LRU
list and keep it for a long time.

A new DCACHE_NEW_NEGATIVE flag is now added to a negative dentry when
it is initially created. When such a dentry is added to the LRU, it
will be added to the head so that it will be the first to go when a
shrinker is running if it is never accessed again. The flag is cleared
after the LRU list addition so that it will be added to the tail like
others if this dentry is pull out from the LRU and put back in again.

Suggested-by: Larry Woodman <lwoodman@redhat.com>
Signed-off-by: Waiman Long <longman@redhat.com>
---
 fs/dcache.c              | 25 +++++++++++++++++--------
 include/linux/dcache.h   |  1 +
 include/linux/list_lru.h | 17 +++++++++++++++++
 mm/list_lru.c            | 19 +++++++++++++++++--
 4 files changed, 52 insertions(+), 10 deletions(-)

diff --git a/fs/dcache.c b/fs/dcache.c
index dbab6c2..0be5972 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -242,12 +242,6 @@ static inline void __neg_dentry_inc(struct dentry *dentry)
 	this_cpu_inc(nr_dentry_neg);
 }
 
-static inline void neg_dentry_inc(struct dentry *dentry)
-{
-	if (unlikely(d_is_negative(dentry)))
-		__neg_dentry_inc(dentry);
-}
-
 static inline int dentry_cmp(const struct dentry *dentry, const unsigned char *ct, unsigned tcount)
 {
 	/*
@@ -352,7 +346,7 @@ static inline void __d_set_inode_and_type(struct dentry *dentry,
 
 	dentry->d_inode = inode;
 	flags = READ_ONCE(dentry->d_flags);
-	flags &= ~(DCACHE_ENTRY_TYPE | DCACHE_FALLTHRU);
+	flags &= ~(DCACHE_ENTRY_TYPE | DCACHE_FALLTHRU | DCACHE_NEW_NEGATIVE);
 	flags |= type_flags;
 	WRITE_ONCE(dentry->d_flags, flags);
 }
@@ -432,8 +426,20 @@ static void d_lru_add(struct dentry *dentry)
 	D_FLAG_VERIFY(dentry, 0);
 	dentry->d_flags |= DCACHE_LRU_LIST;
 	this_cpu_inc(nr_dentry_unused);
+	if (d_is_negative(dentry)) {
+		__neg_dentry_inc(dentry);
+		if (dentry->d_flags & DCACHE_NEW_NEGATIVE) {
+			/*
+			 * Add the negative dentry to the head once, it
+			 * will be added to the tail next time.
+			 */
+			WARN_ON_ONCE(!list_lru_add_head(
+				&dentry->d_sb->s_dentry_lru, &dentry->d_lru));
+			dentry->d_flags &= ~DCACHE_NEW_NEGATIVE;
+			return;
+		}
+	}
 	WARN_ON_ONCE(!list_lru_add(&dentry->d_sb->s_dentry_lru, &dentry->d_lru));
-	neg_dentry_inc(dentry);
 }
 
 static void d_lru_del(struct dentry *dentry)
@@ -2647,6 +2653,9 @@ static inline void __d_add(struct dentry *dentry, struct inode *inode)
 		__d_set_inode_and_type(dentry, inode, add_flags);
 		raw_write_seqcount_end(&dentry->d_seq);
 		fsnotify_update_flags(dentry);
+	} else {
+		/* It is a negative dentry, add it to LRU head initially. */
+		dentry->d_flags |= DCACHE_NEW_NEGATIVE;
 	}
 	__d_rehash(dentry);
 	if (dir)
diff --git a/include/linux/dcache.h b/include/linux/dcache.h
index 6e06d91..89cf348 100644
--- a/include/linux/dcache.h
+++ b/include/linux/dcache.h
@@ -215,6 +215,7 @@ struct dentry_operations {
 #define DCACHE_FALLTHRU			0x01000000 /* Fall through to lower layer */
 #define DCACHE_ENCRYPTED_WITH_KEY	0x02000000 /* dir is encrypted with a valid key */
 #define DCACHE_OP_REAL			0x04000000
+#define DCACHE_NEW_NEGATIVE		0x08000000 /* New negative dentry */
 
 #define DCACHE_PAR_LOOKUP		0x10000000 /* being looked up (with parent locked shared) */
 #define DCACHE_DENTRY_CURSOR		0x20000000
diff --git a/include/linux/list_lru.h b/include/linux/list_lru.h
index 96def9d..1cdbfb0 100644
--- a/include/linux/list_lru.h
+++ b/include/linux/list_lru.h
@@ -86,6 +86,23 @@ int __list_lru_init(struct list_lru *lru, bool memcg_aware,
 bool list_lru_add(struct list_lru *lru, struct list_head *item);
 
 /**
+ * list_lru_add_head: add an element to the lru list's head
+ * @list_lru: the lru pointer
+ * @item: the item to be added.
+ *
+ * This is similar to list_lru_add(). The only difference is the location
+ * where the new item will be added. The list_lru_add() function will add
+ * the new item to the tail as it is the most recently used one. The
+ * list_lru_add_head() will add the new item into the head so that it
+ * will the first to go if a shrinker is running. So this function should
+ * only be used for less important item that can be the first to go if
+ * the system is under memory pressure.
+ *
+ * Return value: true if the list was updated, false otherwise
+ */
+bool list_lru_add_head(struct list_lru *lru, struct list_head *item);
+
+/**
  * list_lru_del: delete an element to the lru list
  * @list_lru: the lru pointer
  * @item: the item to be deleted.
diff --git a/mm/list_lru.c b/mm/list_lru.c
index fcfb6c8..f079fe2 100644
--- a/mm/list_lru.c
+++ b/mm/list_lru.c
@@ -107,7 +107,8 @@ static inline bool list_lru_memcg_aware(struct list_lru *lru)
 }
 #endif /* CONFIG_MEMCG && !CONFIG_SLOB */
 
-bool list_lru_add(struct list_lru *lru, struct list_head *item)
+static inline bool __list_lru_add(struct list_lru *lru, struct list_head *item,
+				  const bool add_tail)
 {
 	int nid = page_to_nid(virt_to_page(item));
 	struct list_lru_node *nlru = &lru->node[nid];
@@ -116,7 +117,10 @@ bool list_lru_add(struct list_lru *lru, struct list_head *item)
 	spin_lock(&nlru->lock);
 	if (list_empty(item)) {
 		l = list_lru_from_kmem(nlru, item);
-		list_add_tail(item, &l->list);
+		if (add_tail)
+			list_add_tail(item, &l->list);
+		else
+			list_add(item, &l->list);
 		l->nr_items++;
 		nlru->nr_items++;
 		spin_unlock(&nlru->lock);
@@ -125,8 +129,19 @@ bool list_lru_add(struct list_lru *lru, struct list_head *item)
 	spin_unlock(&nlru->lock);
 	return false;
 }
+
+bool list_lru_add(struct list_lru *lru, struct list_head *item)
+{
+	return __list_lru_add(lru, item, true);
+}
 EXPORT_SYMBOL_GPL(list_lru_add);
 
+bool list_lru_add_head(struct list_lru *lru, struct list_head *item)
+{
+	return __list_lru_add(lru, item, false);
+}
+EXPORT_SYMBOL_GPL(list_lru_add_head);
+
 bool list_lru_del(struct list_lru *lru, struct list_head *item)
 {
 	int nid = page_to_nid(virt_to_page(item));
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v7 3/6] fs/dcache: Add sysctl parameter neg-dentry-limit as a soft limit on negative dentries
  2018-07-12 16:45 [PATCH v7 0/6] fs/dcache: Track & limit # of negative dentries Waiman Long
  2018-07-12 16:46 ` [PATCH v7 1/6] fs/dcache: Track & report number " Waiman Long
  2018-07-12 16:46 ` [PATCH v7 2/6] fs/dcache: Add negative dentries to LRU head initially Waiman Long
@ 2018-07-12 16:46 ` Waiman Long
  2018-07-12 16:56   ` Matthew Wilcox
  2018-07-12 16:46 ` [PATCH v7 4/6] fs/dcache: Print negative dentry warning every min until turned off by user Waiman Long
                   ` (2 subsequent siblings)
  5 siblings, 1 reply; 11+ messages in thread
From: Waiman Long @ 2018-07-12 16:46 UTC (permalink / raw)
  To: Alexander Viro, Jonathan Corbet, Luis R. Rodriguez, Kees Cook
  Cc: linux-kernel, linux-fsdevel, linux-mm, linux-doc, Linus Torvalds,
	Jan Kara, Paul E. McKenney, Andrew Morton, Ingo Molnar,
	Miklos Szeredi, Matthew Wilcox, Larry Woodman, James Bottomley,
	Wangkai (Kevin C),
	Michal Hocko, Waiman Long

A new sysctl parameter "neg-dentry-limit" is added to /proc/sys/fs
whose value represents a soft limit on the total number of negative
dentries allowable in a system as a multiple of one-thousandth of
the total system memory available.  The allowable range of this new
parameter is 0-100 where 0 means no soft limit.

A warning message will be printed if the soft limit is exceeded. Limit
check will only be done for newly created negative dentries, not
existing ones.

When "neg-dentry-limit" is set to 0 (the default), static key is used to
disable the limit checking code. So the dentry code performance should
be the same as if this patch isn't applied.

As for the proper value to be set in "neg-dentry-limit" for those
users who want it enabled, it will be workload dependent. Whenever a
non-zero value is written into "neg-dentry-limit", an informational
message will be printed about what the actual negative dentry limits
will be. It can be compared against the current negative dentry number
(5th number) from "dentry-state" to see if there is enough safe margin
to avoid false positive warning.

Signed-off-by: Waiman Long <longman@redhat.com>
---
 Documentation/sysctl/fs.txt |  11 +++
 fs/dcache.c                 | 187 ++++++++++++++++++++++++++++++++++++++++++--
 include/linux/dcache.h      |   5 ++
 kernel/sysctl.c             |  11 +++
 4 files changed, 207 insertions(+), 7 deletions(-)

diff --git a/Documentation/sysctl/fs.txt b/Documentation/sysctl/fs.txt
index a8e3f1f..6fd43b3 100644
--- a/Documentation/sysctl/fs.txt
+++ b/Documentation/sysctl/fs.txt
@@ -32,6 +32,7 @@ Currently, these files are in /proc/sys/fs:
 - nr_open
 - overflowuid
 - overflowgid
+- neg-dentry-limit
 - pipe-user-pages-hard
 - pipe-user-pages-soft
 - protected_hardlinks
@@ -168,6 +169,16 @@ The default is 65534.
 
 ==============================================================
 
+neg-dentry-limit:
+
+This integer value specifies a soft limit on the total number of
+negative dentries allowed in a system as a percentage of the total
+system memory available. The allowable range for this value is 0-100.
+A value of 0 means there is no limit. Each unit represents 0.1% of
+the total system memory. So 10% is the maximum that can be specified.
+
+==============================================================
+
 pipe-user-pages-hard:
 
 Maximum total number of pages a non-privileged user may allocate for pipes.
diff --git a/fs/dcache.c b/fs/dcache.c
index 0be5972..1fad368 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -14,6 +14,8 @@
  * the dcache entry is deleted or garbage collected.
  */
 
+#define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt
+
 #include <linux/ratelimit.h>
 #include <linux/string.h>
 #include <linux/mm.h>
@@ -117,6 +119,40 @@ struct dentry_stat_t dentry_stat = {
 	.age_limit = 45,
 };
 
+/*
+ * The sysctl parameter "neg-dentry-limit" specifies the limit for the
+ * number of negative dentries allowable in a system as a multiple of
+ * one-thousandth of the total system memory. The default is 0 which
+ * means there is no limit and the valid range is 0-100. So up to 10%
+ * of the total system memory can be used.
+ *
+ * With a limit of 10 (1%) on a 64-bit system with 1G memory, that
+ * translated to about 50k dentries which is quite a lot.
+ *
+ * To avoid performance problem with a global counter on an SMP system,
+ * the tracking is done mostly on a per-cpu basis. The total limit is
+ * distributed equally in a 1/1 ratio to per-cpu counters and a global
+ * free pool.
+ *
+ * If a per-cpu counter runs out of negative dentries, it can borrow extra
+ * ones from the global free pool. If it has more than its percpu limit,
+ * the extra ones will be returned back to the global pool.
+ */
+#define NEG_DENTRY_BATCH	(1 << 8)
+
+static struct static_key limit_neg_key = STATIC_KEY_INIT_FALSE;
+static int neg_dentry_limit_old;
+int neg_dentry_limit;
+EXPORT_SYMBOL_GPL(neg_dentry_limit);
+
+static long neg_dentry_percpu_limit __read_mostly;
+static long neg_dentry_nfree_init __read_mostly; /* Free pool initial value */
+static struct {
+	raw_spinlock_t nfree_lock;
+	long nfree;			/* Negative dentry free pool */
+} ndblk ____cacheline_aligned_in_smp;
+proc_handler proc_neg_dentry_limit;
+
 static DEFINE_PER_CPU(long, nr_dentry);
 static DEFINE_PER_CPU(long, nr_dentry_unused);
 static DEFINE_PER_CPU(long, nr_dentry_neg);
@@ -160,6 +196,7 @@ static long get_nr_dentry_neg(void)
 
 	for_each_possible_cpu(i)
 		sum += per_cpu(nr_dentry_neg, i);
+	sum += neg_dentry_nfree_init - ndblk.nfree;
 	return sum < 0 ? 0 : sum;
 }
 
@@ -226,9 +263,26 @@ static inline int dentry_string_cmp(const unsigned char *cs, const unsigned char
 
 #endif
 
-static inline void __neg_dentry_dec(struct dentry *dentry)
+/*
+ * Decrement negative dentry count if applicable.
+ */
+static void __neg_dentry_dec(struct dentry *dentry)
 {
-	this_cpu_dec(nr_dentry_neg);
+	if (!static_key_false(&limit_neg_key)) {
+		this_cpu_dec(nr_dentry_neg);
+		return;
+	}
+
+	if (unlikely(this_cpu_dec_return(nr_dentry_neg) < 0)) {
+		long *pcnt = get_cpu_ptr(&nr_dentry_neg);
+
+		if ((*pcnt < 0) && raw_spin_trylock(&ndblk.nfree_lock)) {
+			WRITE_ONCE(ndblk.nfree, ndblk.nfree + NEG_DENTRY_BATCH);
+			*pcnt += NEG_DENTRY_BATCH;
+			raw_spin_unlock(&ndblk.nfree_lock);
+		}
+		put_cpu_ptr(&nr_dentry_neg);
+	}
 }
 
 static inline void neg_dentry_dec(struct dentry *dentry)
@@ -237,11 +291,120 @@ static inline void neg_dentry_dec(struct dentry *dentry)
 		__neg_dentry_dec(dentry);
 }
 
-static inline void __neg_dentry_inc(struct dentry *dentry)
+/*
+ * Try to decrement the negative dentry free pool by NEG_DENTRY_BATCH.
+ * The actual decrement returned by the function may be smaller.
+ */
+static long __neg_dentry_nfree_dec(long cnt)
 {
-	this_cpu_inc(nr_dentry_neg);
+	cnt = max_t(long, NEG_DENTRY_BATCH, cnt);
+	raw_spin_lock(&ndblk.nfree_lock);
+	if (ndblk.nfree < cnt)
+		cnt = (ndblk.nfree > 0) ? ndblk.nfree : 0;
+	WRITE_ONCE(ndblk.nfree, ndblk.nfree - cnt);
+	raw_spin_unlock(&ndblk.nfree_lock);
+	return cnt;
 }
 
+static noinline void neg_dentry_inc_slowpath(struct dentry *dentry)
+{
+	long cnt = 0, *pcnt;
+
+	/*
+	 * Try to move some negative dentry quota from the global free
+	 * pool to the percpu count to allow more negative dentries to
+	 * be added to the LRU.
+	 */
+	pcnt = get_cpu_ptr(&nr_dentry_neg);
+	if ((READ_ONCE(ndblk.nfree) > 0) &&
+	    (*pcnt > neg_dentry_percpu_limit)) {
+		cnt = __neg_dentry_nfree_dec(*pcnt - neg_dentry_percpu_limit);
+		*pcnt -= cnt;
+	}
+	put_cpu_ptr(&nr_dentry_neg);
+
+	/*
+	 * Put out a warning if there are too many negative dentries.
+	 */
+	if (!cnt)
+		pr_warn_once("There are too many negative dentries.");
+
+}
+
+/*
+ * Increment negative dentry count if applicable.
+ */
+static void neg_dentry_inc(struct dentry *dentry)
+{
+	if (!static_key_false(&limit_neg_key)) {
+		this_cpu_inc(nr_dentry_neg);
+		return;
+	}
+
+	if (likely(this_cpu_inc_return(nr_dentry_neg) <=
+		   neg_dentry_percpu_limit))
+		return;
+
+	neg_dentry_inc_slowpath(dentry);
+}
+
+/*
+ * Sysctl proc handler for neg_dentry_limit.
+ */
+int proc_neg_dentry_limit(struct ctl_table *ctl, int write,
+			  void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+	/* Rough estimate of # of dentries allocated per page */
+	const unsigned int nr_dentry_page = PAGE_SIZE/sizeof(struct dentry) - 1;
+	unsigned long cnt, new_init;
+	int ret, ncpus;
+
+	ret = proc_dointvec_minmax(ctl, write, buffer, lenp, ppos);
+
+	if (!write || ret || (neg_dentry_limit == neg_dentry_limit_old))
+		return ret;
+
+	/*
+	 * Disable limit_neg_key first when transitioning from
+	 * neg_dentry_limit to !neg_dentry_limit. In this case, we freeze
+	 * whatever value is in neg_dentry_nfree_init and return.
+	 */
+	if (!neg_dentry_limit && neg_dentry_limit_old) {
+		static_key_slow_dec(&limit_neg_key);
+		goto out;
+	}
+
+	raw_spin_lock(&ndblk.nfree_lock);
+
+	/* 50% in global pool & 50% in percpu free */
+	new_init = totalram_pages * nr_dentry_page * neg_dentry_limit / 2000;
+	ncpus = num_possible_cpus();
+	cnt = new_init / ncpus;
+	if (cnt < 2 * NEG_DENTRY_BATCH)
+		cnt = 2 * NEG_DENTRY_BATCH;
+	neg_dentry_percpu_limit = cnt;
+
+	/*
+	 * Any change in neg_dentry_nfree_init must be applied to ndblk.nfree
+	 * as well. The ndblk.nfree value may become negative if there is
+	 * a decrease in percentage.
+	 */
+	ndblk.nfree += new_init - neg_dentry_nfree_init;
+	neg_dentry_nfree_init = new_init;
+	raw_spin_unlock(&ndblk.nfree_lock);
+
+	pr_info("Negative dentry limits: percpu = %ld, total = %ld\n",
+		neg_dentry_percpu_limit,
+		neg_dentry_nfree_init + neg_dentry_percpu_limit * ncpus);
+
+	if (!neg_dentry_limit_old)
+		static_key_slow_inc(&limit_neg_key);
+out:
+	neg_dentry_limit_old = neg_dentry_limit;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(proc_neg_dentry_limit);
+
 static inline int dentry_cmp(const struct dentry *dentry, const unsigned char *ct, unsigned tcount)
 {
 	/*
@@ -358,8 +521,10 @@ static inline void __d_clear_type_and_inode(struct dentry *dentry)
 	flags &= ~(DCACHE_ENTRY_TYPE | DCACHE_FALLTHRU);
 	WRITE_ONCE(dentry->d_flags, flags);
 	dentry->d_inode = NULL;
+
+	/* We don't do limit check in pos->neg dentry transitions */
 	if (dentry->d_flags & DCACHE_LRU_LIST)
-		__neg_dentry_inc(dentry);
+		this_cpu_inc(nr_dentry_neg);
 }
 
 static void dentry_free(struct dentry *dentry)
@@ -427,17 +592,23 @@ static void d_lru_add(struct dentry *dentry)
 	dentry->d_flags |= DCACHE_LRU_LIST;
 	this_cpu_inc(nr_dentry_unused);
 	if (d_is_negative(dentry)) {
-		__neg_dentry_inc(dentry);
 		if (dentry->d_flags & DCACHE_NEW_NEGATIVE) {
+			dentry->d_flags &= ~DCACHE_NEW_NEGATIVE;
+			neg_dentry_inc(dentry);
+
 			/*
 			 * Add the negative dentry to the head once, it
 			 * will be added to the tail next time.
 			 */
 			WARN_ON_ONCE(!list_lru_add_head(
 				&dentry->d_sb->s_dentry_lru, &dentry->d_lru));
-			dentry->d_flags &= ~DCACHE_NEW_NEGATIVE;
 			return;
 		}
+		/*
+		 * We don't do limit check for existing negative
+		 * dentries.
+		 */
+		this_cpu_inc(nr_dentry_neg);
 	}
 	WARN_ON_ONCE(!list_lru_add(&dentry->d_sb->s_dentry_lru, &dentry->d_lru));
 }
@@ -3200,6 +3371,8 @@ static void __init dcache_init(void)
 		SLAB_RECLAIM_ACCOUNT|SLAB_PANIC|SLAB_MEM_SPREAD|SLAB_ACCOUNT,
 		d_iname);
 
+	raw_spin_lock_init(&ndblk.nfree_lock);
+
 	/* Hash may have been set up in dcache_init_early */
 	if (!hashdist)
 		return;
diff --git a/include/linux/dcache.h b/include/linux/dcache.h
index 89cf348..4216eca 100644
--- a/include/linux/dcache.h
+++ b/include/linux/dcache.h
@@ -611,4 +611,9 @@ struct name_snapshot {
 void take_dentry_name_snapshot(struct name_snapshot *, struct dentry *);
 void release_dentry_name_snapshot(struct name_snapshot *);
 
+/*
+ * Negative dentry related declarations.
+ */
+extern int neg_dentry_limit;
+
 #endif	/* __LINUX_DCACHE_H */
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index 2d9837c..2877782 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -114,6 +114,8 @@
 extern int sysctl_nr_trim_pages;
 #endif
 
+extern proc_handler proc_neg_dentry_limit;
+
 /* Constants used for minimum and  maximum */
 #ifdef CONFIG_LOCKUP_DETECTOR
 static int sixty = 60;
@@ -1849,6 +1851,15 @@ static int sysrq_sysctl_handler(struct ctl_table *table, int write,
 		.proc_handler	= proc_dointvec_minmax,
 		.extra1		= &one,
 	},
+	{
+		.procname	= "neg-dentry-limit",
+		.data		= &neg_dentry_limit,
+		.maxlen		= sizeof(neg_dentry_limit),
+		.mode		= 0644,
+		.proc_handler	= proc_neg_dentry_limit,
+		.extra1		= &zero,
+		.extra2		= &one_hundred,
+	},
 	{ }
 };
 
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v7 4/6] fs/dcache: Print negative dentry warning every min until turned off by user
  2018-07-12 16:45 [PATCH v7 0/6] fs/dcache: Track & limit # of negative dentries Waiman Long
                   ` (2 preceding siblings ...)
  2018-07-12 16:46 ` [PATCH v7 3/6] fs/dcache: Add sysctl parameter neg-dentry-limit as a soft limit on negative dentries Waiman Long
@ 2018-07-12 16:46 ` Waiman Long
  2018-07-17  1:24   ` kbuild test robot
  2018-07-12 16:46 ` [PATCH v7 5/6] fs/dcache: Allow optional enforcement of negative dentry limit Waiman Long
  2018-07-12 16:46 ` [PATCH v7 6/6] fs/dcache: Allow deconfiguration of negative dentry code to reduce kernel size Waiman Long
  5 siblings, 1 reply; 11+ messages in thread
From: Waiman Long @ 2018-07-12 16:46 UTC (permalink / raw)
  To: Alexander Viro, Jonathan Corbet, Luis R. Rodriguez, Kees Cook
  Cc: linux-kernel, linux-fsdevel, linux-mm, linux-doc, Linus Torvalds,
	Jan Kara, Paul E. McKenney, Andrew Morton, Ingo Molnar,
	Miklos Szeredi, Matthew Wilcox, Larry Woodman, James Bottomley,
	Wangkai (Kevin C),
	Michal Hocko, Waiman Long

When there are too many negative dentries, printing a warning once may
not get the attention of the system administrator. So it is now change
to print a warning every minute until it is turned off by either writing
a 0 into fs/neg-dentry-limit or the limit is increased. After that the
system administrator can look into the reason why there are so many
negative dentries.

Note that the warning is printed when the global negative dentry free
pool is depleted even if there are space in other percpu negative dentry
counts for more.

Signed-off-by: Waiman Long <longman@redhat.com>
---
 fs/dcache.c | 35 ++++++++++++++++++++++++++++++++---
 1 file changed, 32 insertions(+), 3 deletions(-)

diff --git a/fs/dcache.c b/fs/dcache.c
index 1fad368..b2c1585 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -139,6 +139,7 @@ struct dentry_stat_t dentry_stat = {
  * the extra ones will be returned back to the global pool.
  */
 #define NEG_DENTRY_BATCH	(1 << 8)
+#define NEG_WARN_PERIOD 	(60 * HZ)	/* Print a warning every min */
 
 static struct static_key limit_neg_key = STATIC_KEY_INIT_FALSE;
 static int neg_dentry_limit_old;
@@ -150,6 +151,7 @@ struct dentry_stat_t dentry_stat = {
 static struct {
 	raw_spinlock_t nfree_lock;
 	long nfree;			/* Negative dentry free pool */
+	unsigned long warn_jiffies;	/* Time when last warning is printed */
 } ndblk ____cacheline_aligned_in_smp;
 proc_handler proc_neg_dentry_limit;
 
@@ -309,6 +311,7 @@ static long __neg_dentry_nfree_dec(long cnt)
 static noinline void neg_dentry_inc_slowpath(struct dentry *dentry)
 {
 	long cnt = 0, *pcnt;
+	unsigned long current_time;
 
 	/*
 	 * Try to move some negative dentry quota from the global free
@@ -323,12 +326,38 @@ static noinline void neg_dentry_inc_slowpath(struct dentry *dentry)
 	}
 	put_cpu_ptr(&nr_dentry_neg);
 
+	if (cnt)
+		goto out;
+
 	/*
-	 * Put out a warning if there are too many negative dentries.
+	 * Put out a warning every minute or so if there are just too many
+	 * negative dentries.
 	 */
-	if (!cnt)
-		pr_warn_once("There are too many negative dentries.");
+	current_time = jiffies;
+
+	if (current_time < ndblk.warn_jiffies + NEG_WARN_PERIOD)
+		goto out;
+	/*
+	 * Update the time in ndblk.warn_jiffies and print a warning
+	 * if time update is successful.
+	 */
+	raw_spin_lock(&ndblk.nfree_lock);
+	if (current_time < ndblk.warn_jiffies + NEG_WARN_PERIOD) {
+		raw_spin_unlock(&ndblk.nfree_lock);
+		goto out;
+	}
+	ndblk.warn_jiffies = current_time;
+	raw_spin_unlock(&ndblk.nfree_lock);
 
+	/*
+	 * Get the current negative dentry count & print a warning.
+	 */
+	cnt = get_nr_dentry_neg();
+	pr_warn("Warning: Too many negative dentries (%ld). "
+		"This warning can be disabled by writing 0 to \"fs/neg-dentry-limit\" or increasing the limit.\n",
+		cnt);
+out:
+	return;
 }
 
 /*
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v7 5/6] fs/dcache: Allow optional enforcement of negative dentry limit
  2018-07-12 16:45 [PATCH v7 0/6] fs/dcache: Track & limit # of negative dentries Waiman Long
                   ` (3 preceding siblings ...)
  2018-07-12 16:46 ` [PATCH v7 4/6] fs/dcache: Print negative dentry warning every min until turned off by user Waiman Long
@ 2018-07-12 16:46 ` Waiman Long
  2018-07-12 16:46 ` [PATCH v7 6/6] fs/dcache: Allow deconfiguration of negative dentry code to reduce kernel size Waiman Long
  5 siblings, 0 replies; 11+ messages in thread
From: Waiman Long @ 2018-07-12 16:46 UTC (permalink / raw)
  To: Alexander Viro, Jonathan Corbet, Luis R. Rodriguez, Kees Cook
  Cc: linux-kernel, linux-fsdevel, linux-mm, linux-doc, Linus Torvalds,
	Jan Kara, Paul E. McKenney, Andrew Morton, Ingo Molnar,
	Miklos Szeredi, Matthew Wilcox, Larry Woodman, James Bottomley,
	Wangkai (Kevin C),
	Michal Hocko, Waiman Long

If a rogue application that generates a large number of negative dentries
is running, it is possible that negative dentries will use up most of
the available memory in the system when that application is not under
the control of a memory cgroup that limit kernel memory.

The lack of available memory may significantly impact the operation
of other applications running in the system. It will slow down system
performance and may even work as part of a DoS attack on the system.

To allow system administrators the option to prevent this extreme
situation from happening, a new "neg-dentry-enforce" sysctl parameter
is now added which can be set to to enforce the negative dentry soft
limit set in "neg-dentry-limit" so that it becomes a hard limit. When
the limit is enforced, extra negative dentries that exceed the limit
will be killed after use instead of leaving them in the LRU.

Note that negative dentry killing happens when the global negative
dentry free pool is depleted even if there are space in other percpu
negative dentry counts for more.

Signed-off-by: Waiman Long <longman@redhat.com>
---
 Documentation/sysctl/fs.txt | 10 +++++++++
 fs/dcache.c                 | 51 ++++++++++++++++++++++++++++++++++++---------
 include/linux/dcache.h      |  2 ++
 kernel/sysctl.c             |  9 ++++++++
 4 files changed, 62 insertions(+), 10 deletions(-)

diff --git a/Documentation/sysctl/fs.txt b/Documentation/sysctl/fs.txt
index 6fd43b3..b073d9a 100644
--- a/Documentation/sysctl/fs.txt
+++ b/Documentation/sysctl/fs.txt
@@ -32,6 +32,7 @@ Currently, these files are in /proc/sys/fs:
 - nr_open
 - overflowuid
 - overflowgid
+- neg-dentry-enforce
 - neg-dentry-limit
 - pipe-user-pages-hard
 - pipe-user-pages-soft
@@ -169,6 +170,15 @@ The default is 65534.
 
 ==============================================================
 
+neg-dentry-enforce:
+
+The file neg-dentry-enforce, if present, contains a boolean flag (0 or
+1) indicating if the negative dentries limit set by the "neg-dentry-limit"
+sysctl parameter should be enforced or not.  If enforced, excess negative
+dentries over the limit will be killed immediately after use.
+
+==============================================================
+
 neg-dentry-limit:
 
 This integer value specifies a soft limit on the total number of
diff --git a/fs/dcache.c b/fs/dcache.c
index b2c1585..843c8be 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -145,6 +145,8 @@ struct dentry_stat_t dentry_stat = {
 static int neg_dentry_limit_old;
 int neg_dentry_limit;
 EXPORT_SYMBOL_GPL(neg_dentry_limit);
+int neg_dentry_enforce;	/* Enforce the negative dentry limit */
+EXPORT_SYMBOL_GPL(neg_dentry_enforce);
 
 static long neg_dentry_percpu_limit __read_mostly;
 static long neg_dentry_nfree_init __read_mostly; /* Free pool initial value */
@@ -308,7 +310,7 @@ static long __neg_dentry_nfree_dec(long cnt)
 	return cnt;
 }
 
-static noinline void neg_dentry_inc_slowpath(struct dentry *dentry)
+static noinline int neg_dentry_inc_slowpath(struct dentry *dentry)
 {
 	long cnt = 0, *pcnt;
 	unsigned long current_time;
@@ -330,6 +332,24 @@ static noinline void neg_dentry_inc_slowpath(struct dentry *dentry)
 		goto out;
 
 	/*
+	 * Kill the dentry by setting the DCACHE_KILL_NEGATIVE flag and
+	 * dec the negative dentry count if the enforcing option is on.
+	 */
+	if (neg_dentry_enforce) {
+		dentry->d_flags |= DCACHE_KILL_NEGATIVE;
+		this_cpu_dec(nr_dentry_neg);
+
+		/*
+		 * When the dentry is not put into the LRU, we
+		 * need to keep the reference count to 1 to
+		 * avoid problem when killing it.
+		 */
+		WARN_ON_ONCE(dentry->d_lockref.count);
+		dentry->d_lockref.count = 1;
+		return -1; /* Kill the dentry now */
+	}
+
+	/*
 	 * Put out a warning every minute or so if there are just too many
 	 * negative dentries.
 	 */
@@ -354,27 +374,28 @@ static noinline void neg_dentry_inc_slowpath(struct dentry *dentry)
 	 */
 	cnt = get_nr_dentry_neg();
 	pr_warn("Warning: Too many negative dentries (%ld). "
-		"This warning can be disabled by writing 0 to \"fs/neg-dentry-limit\" or increasing the limit.\n",
+		"This warning can be disabled by writing 0 to \"fs/neg-dentry-limit\", increasing the limit or writing 1 to \"fs/neg-dentry-enforce\".\n",
 		cnt);
 out:
-	return;
+	return 0;
 }
 
 /*
  * Increment negative dentry count if applicable.
+ * Return: 0 on success, -1 to kill it.
  */
-static void neg_dentry_inc(struct dentry *dentry)
+static int neg_dentry_inc(struct dentry *dentry)
 {
 	if (!static_key_false(&limit_neg_key)) {
 		this_cpu_inc(nr_dentry_neg);
-		return;
+		return 0;
 	}
 
 	if (likely(this_cpu_inc_return(nr_dentry_neg) <=
 		   neg_dentry_percpu_limit))
-		return;
+		return 0;
 
-	neg_dentry_inc_slowpath(dentry);
+	return neg_dentry_inc_slowpath(dentry);
 }
 
 /*
@@ -623,7 +644,11 @@ static void d_lru_add(struct dentry *dentry)
 	if (d_is_negative(dentry)) {
 		if (dentry->d_flags & DCACHE_NEW_NEGATIVE) {
 			dentry->d_flags &= ~DCACHE_NEW_NEGATIVE;
-			neg_dentry_inc(dentry);
+			if (unlikely(neg_dentry_inc(dentry) < 0)) {
+				this_cpu_dec(nr_dentry_unused);
+				dentry->d_flags &= ~DCACHE_LRU_LIST;
+				return;	/* To be killed */
+			}
 
 			/*
 			 * Add the negative dentry to the head once, it
@@ -878,16 +903,22 @@ static inline bool retain_dentry(struct dentry *dentry)
 	if (unlikely(dentry->d_flags & DCACHE_DISCONNECTED))
 		return false;
 
+	if (unlikely(dentry->d_flags & DCACHE_KILL_NEGATIVE))
+		return false;
+
 	if (unlikely(dentry->d_flags & DCACHE_OP_DELETE)) {
 		if (dentry->d_op->d_delete(dentry))
 			return false;
 	}
 	/* retain; LRU fodder */
 	dentry->d_lockref.count--;
-	if (unlikely(!(dentry->d_flags & DCACHE_LRU_LIST)))
+	if (unlikely(!(dentry->d_flags & DCACHE_LRU_LIST))) {
 		d_lru_add(dentry);
-	else if (unlikely(!(dentry->d_flags & DCACHE_REFERENCED)))
+		if (unlikely(dentry->d_flags & DCACHE_KILL_NEGATIVE))
+			return false;
+	} else if (unlikely(!(dentry->d_flags & DCACHE_REFERENCED))) {
 		dentry->d_flags |= DCACHE_REFERENCED;
+	}
 	return true;
 }
 
diff --git a/include/linux/dcache.h b/include/linux/dcache.h
index 4216eca..934a6d9 100644
--- a/include/linux/dcache.h
+++ b/include/linux/dcache.h
@@ -219,6 +219,7 @@ struct dentry_operations {
 
 #define DCACHE_PAR_LOOKUP		0x10000000 /* being looked up (with parent locked shared) */
 #define DCACHE_DENTRY_CURSOR		0x20000000
+#define DCACHE_KILL_NEGATIVE		0x40000000 /* Kill negative dentry */
 
 extern seqlock_t rename_lock;
 
@@ -615,5 +616,6 @@ struct name_snapshot {
  * Negative dentry related declarations.
  */
 extern int neg_dentry_limit;
+extern int neg_dentry_enforce;
 
 #endif	/* __LINUX_DCACHE_H */
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index 2877782..a24101e 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -1860,6 +1860,15 @@ static int sysrq_sysctl_handler(struct ctl_table *table, int write,
 		.extra1		= &zero,
 		.extra2		= &one_hundred,
 	},
+	{
+		.procname	= "neg-dentry-enforce",
+		.data		= &neg_dentry_enforce,
+		.maxlen		= sizeof(neg_dentry_enforce),
+		.mode		= 0644,
+		.proc_handler	= proc_dointvec_minmax,
+		.extra1		= &zero,
+		.extra2		= &one,
+	},
 	{ }
 };
 
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v7 6/6] fs/dcache: Allow deconfiguration of negative dentry code to reduce kernel size
  2018-07-12 16:45 [PATCH v7 0/6] fs/dcache: Track & limit # of negative dentries Waiman Long
                   ` (4 preceding siblings ...)
  2018-07-12 16:46 ` [PATCH v7 5/6] fs/dcache: Allow optional enforcement of negative dentry limit Waiman Long
@ 2018-07-12 16:46 ` Waiman Long
  2018-07-17  1:25   ` kbuild test robot
  5 siblings, 1 reply; 11+ messages in thread
From: Waiman Long @ 2018-07-12 16:46 UTC (permalink / raw)
  To: Alexander Viro, Jonathan Corbet, Luis R. Rodriguez, Kees Cook
  Cc: linux-kernel, linux-fsdevel, linux-mm, linux-doc, Linus Torvalds,
	Jan Kara, Paul E. McKenney, Andrew Morton, Ingo Molnar,
	Miklos Szeredi, Matthew Wilcox, Larry Woodman, James Bottomley,
	Wangkai (Kevin C),
	Michal Hocko, Waiman Long

The tracking and limit of negative dentries in a filesystem is a useful
addition. However, for users who want to reduce the kernel size as much
as possible, this feature will probably be on the chopping block. To
suit those users, a default-y config option DCACHE_LIMIT_NEG_ENTRY is
added so that the negative dentry limiting code can be configured out,
if necessary.

Signed-off-by: Waiman Long <longman@redhat.com>
---
 fs/Kconfig             | 10 ++++++++++
 fs/dcache.c            | 29 +++++++++++++++++++++++------
 include/linux/dcache.h |  2 ++
 kernel/sysctl.c        |  2 ++
 4 files changed, 37 insertions(+), 6 deletions(-)

diff --git a/fs/Kconfig b/fs/Kconfig
index ac474a6..b521941 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -113,6 +113,16 @@ source "fs/autofs/Kconfig"
 source "fs/fuse/Kconfig"
 source "fs/overlayfs/Kconfig"
 
+#
+# Track and limit the number of negative dentries allowed in the system.
+#
+config DCACHE_LIMIT_NEG_ENTRY
+	bool "Track & limit negative dcache entries"
+	default y
+	help
+	  This option enables the tracking and limiting of the total
+	  number of negative dcache entries allowable in the filesystem.
+
 menu "Caches"
 
 source "fs/fscache/Kconfig"
diff --git a/fs/dcache.c b/fs/dcache.c
index 843c8be..dccfe39 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -141,6 +141,7 @@ struct dentry_stat_t dentry_stat = {
 #define NEG_DENTRY_BATCH	(1 << 8)
 #define NEG_WARN_PERIOD 	(60 * HZ)	/* Print a warning every min */
 
+#ifdef CONFIG_DCACHE_LIMIT_NEG_ENTRY
 static struct static_key limit_neg_key = STATIC_KEY_INIT_FALSE;
 static int neg_dentry_limit_old;
 int neg_dentry_limit;
@@ -156,6 +157,7 @@ struct dentry_stat_t dentry_stat = {
 	unsigned long warn_jiffies;	/* Time when last warning is printed */
 } ndblk ____cacheline_aligned_in_smp;
 proc_handler proc_neg_dentry_limit;
+#endif /* CONFIG_DCACHE_LIMIT_NEG_ENTRY */
 
 static DEFINE_PER_CPU(long, nr_dentry);
 static DEFINE_PER_CPU(long, nr_dentry_unused);
@@ -200,7 +202,9 @@ static long get_nr_dentry_neg(void)
 
 	for_each_possible_cpu(i)
 		sum += per_cpu(nr_dentry_neg, i);
+#ifdef CONFIG_DCACHE_LIMIT_NEG_ENTRY
 	sum += neg_dentry_nfree_init - ndblk.nfree;
+#endif
 	return sum < 0 ? 0 : sum;
 }
 
@@ -267,6 +271,7 @@ static inline int dentry_string_cmp(const unsigned char *cs, const unsigned char
 
 #endif
 
+#ifdef CONFIG_DCACHE_LIMIT_NEG_ENTRY
 /*
  * Decrement negative dentry count if applicable.
  */
@@ -289,12 +294,6 @@ static void __neg_dentry_dec(struct dentry *dentry)
 	}
 }
 
-static inline void neg_dentry_dec(struct dentry *dentry)
-{
-	if (unlikely(d_is_negative(dentry)))
-		__neg_dentry_dec(dentry);
-}
-
 /*
  * Try to decrement the negative dentry free pool by NEG_DENTRY_BATCH.
  * The actual decrement returned by the function may be smaller.
@@ -454,6 +453,20 @@ int proc_neg_dentry_limit(struct ctl_table *ctl, int write,
 	return 0;
 }
 EXPORT_SYMBOL_GPL(proc_neg_dentry_limit);
+#else /* CONFIG_DCACHE_LIMIT_NEG_ENTRY */
+
+static inline void __neg_dentry_dec(struct dentry *dentry)
+{
+	 this_cpu_dec(nr_dentry_neg);
+}
+
+#endif /* CONFIG_DCACHE_LIMIT_NEG_ENTRY */
+
+static inline void neg_dentry_dec(struct dentry *dentry)
+{
+	if (unlikely(d_is_negative(dentry)))
+		__neg_dentry_dec(dentry);
+}
 
 static inline int dentry_cmp(const struct dentry *dentry, const unsigned char *ct, unsigned tcount)
 {
@@ -642,6 +655,7 @@ static void d_lru_add(struct dentry *dentry)
 	dentry->d_flags |= DCACHE_LRU_LIST;
 	this_cpu_inc(nr_dentry_unused);
 	if (d_is_negative(dentry)) {
+#ifdef CONFIG_DCACHE_LIMIT_NEG_ENTRY
 		if (dentry->d_flags & DCACHE_NEW_NEGATIVE) {
 			dentry->d_flags &= ~DCACHE_NEW_NEGATIVE;
 			if (unlikely(neg_dentry_inc(dentry) < 0)) {
@@ -658,6 +672,7 @@ static void d_lru_add(struct dentry *dentry)
 				&dentry->d_sb->s_dentry_lru, &dentry->d_lru));
 			return;
 		}
+#endif
 		/*
 		 * We don't do limit check for existing negative
 		 * dentries.
@@ -3431,7 +3446,9 @@ static void __init dcache_init(void)
 		SLAB_RECLAIM_ACCOUNT|SLAB_PANIC|SLAB_MEM_SPREAD|SLAB_ACCOUNT,
 		d_iname);
 
+#ifdef CONFIG_DCACHE_LIMIT_NEG_ENTRY
 	raw_spin_lock_init(&ndblk.nfree_lock);
+#endif
 
 	/* Hash may have been set up in dcache_init_early */
 	if (!hashdist)
diff --git a/include/linux/dcache.h b/include/linux/dcache.h
index 934a6d9..11729a1 100644
--- a/include/linux/dcache.h
+++ b/include/linux/dcache.h
@@ -612,10 +612,12 @@ struct name_snapshot {
 void take_dentry_name_snapshot(struct name_snapshot *, struct dentry *);
 void release_dentry_name_snapshot(struct name_snapshot *);
 
+#ifdef CONFIG_DCACHE_LIMIT_NEG_ENTRY
 /*
  * Negative dentry related declarations.
  */
 extern int neg_dentry_limit;
 extern int neg_dentry_enforce;
+#endif
 
 #endif	/* __LINUX_DCACHE_H */
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index a24101e..732c624 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -1851,6 +1851,7 @@ static int sysrq_sysctl_handler(struct ctl_table *table, int write,
 		.proc_handler	= proc_dointvec_minmax,
 		.extra1		= &one,
 	},
+#ifdef CONFIG_DCACHE_LIMIT_NEG_ENTRY
 	{
 		.procname	= "neg-dentry-limit",
 		.data		= &neg_dentry_limit,
@@ -1869,6 +1870,7 @@ static int sysrq_sysctl_handler(struct ctl_table *table, int write,
 		.extra1		= &zero,
 		.extra2		= &one,
 	},
+#endif
 	{ }
 };
 
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [PATCH v7 3/6] fs/dcache: Add sysctl parameter neg-dentry-limit as a soft limit on negative dentries
  2018-07-12 16:46 ` [PATCH v7 3/6] fs/dcache: Add sysctl parameter neg-dentry-limit as a soft limit on negative dentries Waiman Long
@ 2018-07-12 16:56   ` Matthew Wilcox
  2018-07-12 17:04     ` Waiman Long
  0 siblings, 1 reply; 11+ messages in thread
From: Matthew Wilcox @ 2018-07-12 16:56 UTC (permalink / raw)
  To: Waiman Long
  Cc: Alexander Viro, Jonathan Corbet, Luis R. Rodriguez, Kees Cook,
	linux-kernel, linux-fsdevel, linux-mm, linux-doc, Linus Torvalds,
	Jan Kara, Paul E. McKenney, Andrew Morton, Ingo Molnar,
	Miklos Szeredi, Larry Woodman, James Bottomley, Wangkai (Kevin C),
	Michal Hocko

On Thu, Jul 12, 2018 at 12:46:02PM -0400, Waiman Long wrote:
> +int neg_dentry_limit;
> +EXPORT_SYMBOL_GPL(neg_dentry_limit);

Why are you exporting it?  What module needs this?

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v7 3/6] fs/dcache: Add sysctl parameter neg-dentry-limit as a soft limit on negative dentries
  2018-07-12 16:56   ` Matthew Wilcox
@ 2018-07-12 17:04     ` Waiman Long
  0 siblings, 0 replies; 11+ messages in thread
From: Waiman Long @ 2018-07-12 17:04 UTC (permalink / raw)
  To: Matthew Wilcox
  Cc: Alexander Viro, Jonathan Corbet, Luis R. Rodriguez, Kees Cook,
	linux-kernel, linux-fsdevel, linux-mm, linux-doc, Linus Torvalds,
	Jan Kara, Paul E. McKenney, Andrew Morton, Ingo Molnar,
	Miklos Szeredi, Larry Woodman, James Bottomley, Wangkai (Kevin C),
	Michal Hocko

On 07/12/2018 12:56 PM, Matthew Wilcox wrote:
> On Thu, Jul 12, 2018 at 12:46:02PM -0400, Waiman Long wrote:
>> +int neg_dentry_limit;
>> +EXPORT_SYMBOL_GPL(neg_dentry_limit);
> Why are you exporting it?  What module needs this?

I was following the example of another sysctl parameter in dcache.c -
sysctl_vfs_cache_pressure. Looking more carefully now, you are right
that I don't need to do the exporting. Will fix that in the next update.

Thanks,
Longman

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v7 4/6] fs/dcache: Print negative dentry warning every min until turned off by user
  2018-07-12 16:46 ` [PATCH v7 4/6] fs/dcache: Print negative dentry warning every min until turned off by user Waiman Long
@ 2018-07-17  1:24   ` kbuild test robot
  0 siblings, 0 replies; 11+ messages in thread
From: kbuild test robot @ 2018-07-17  1:24 UTC (permalink / raw)
  To: Waiman Long
  Cc: kbuild-all, Alexander Viro, Jonathan Corbet, Luis R. Rodriguez,
	Kees Cook, linux-kernel, linux-fsdevel, linux-mm, linux-doc,
	Linus Torvalds, Jan Kara, Paul E. McKenney, Andrew Morton,
	Ingo Molnar, Miklos Szeredi, Matthew Wilcox, Larry Woodman,
	James Bottomley, Wangkai (Kevin C),
	Michal Hocko, Waiman Long

[-- Attachment #1: Type: text/plain, Size: 4995 bytes --]

Hi Waiman,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on linus/master]
[also build test ERROR on v4.18-rc4]
[cannot apply to next-20180713]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Waiman-Long/fs-dcache-Track-report-number-of-negative-dentries/20180714-161258
config: i386-tinyconfig (attached as .config)
compiler: gcc-7 (Debian 7.3.0-16) 7.3.0
reproduce:
        # save the attached .config to linux build tree
        make ARCH=i386 
:::::: branch date: 45 minutes ago
:::::: commit date: 45 minutes ago

Note: the linux-review/Waiman-Long/fs-dcache-Track-report-number-of-negative-dentries/20180714-161258 HEAD ca68ee513a450445b269248c2859302c8931a294 builds fine.
      It only hurts bisectibility.

All errors (new ones prefixed by >>):

   fs/dcache.c: In function 'neg_dentry_inc_slowpath':
>> fs/dcache.c:355:8: error: implicit declaration of function 'get_nr_dentry_neg'; did you mean 'neg_dentry_dec'? [-Werror=implicit-function-declaration]
     cnt = get_nr_dentry_neg();
           ^~~~~~~~~~~~~~~~~
           neg_dentry_dec
   cc1: some warnings being treated as errors

# https://github.com/0day-ci/linux/commit/2aa8bf4658af0dbc07ae9ea07d04937a347e3ef4
git remote add linux-review https://github.com/0day-ci/linux
git remote update linux-review
git checkout 2aa8bf4658af0dbc07ae9ea07d04937a347e3ef4
vim +355 fs/dcache.c

bcc9ba8b Waiman Long 2018-07-12  310  
bcc9ba8b Waiman Long 2018-07-12  311  static noinline void neg_dentry_inc_slowpath(struct dentry *dentry)
bcc9ba8b Waiman Long 2018-07-12  312  {
bcc9ba8b Waiman Long 2018-07-12  313  	long cnt = 0, *pcnt;
2aa8bf46 Waiman Long 2018-07-12  314  	unsigned long current_time;
bcc9ba8b Waiman Long 2018-07-12  315  
bcc9ba8b Waiman Long 2018-07-12  316  	/*
bcc9ba8b Waiman Long 2018-07-12  317  	 * Try to move some negative dentry quota from the global free
bcc9ba8b Waiman Long 2018-07-12  318  	 * pool to the percpu count to allow more negative dentries to
bcc9ba8b Waiman Long 2018-07-12  319  	 * be added to the LRU.
bcc9ba8b Waiman Long 2018-07-12  320  	 */
bcc9ba8b Waiman Long 2018-07-12  321  	pcnt = get_cpu_ptr(&nr_dentry_neg);
bcc9ba8b Waiman Long 2018-07-12  322  	if ((READ_ONCE(ndblk.nfree) > 0) &&
bcc9ba8b Waiman Long 2018-07-12  323  	    (*pcnt > neg_dentry_percpu_limit)) {
bcc9ba8b Waiman Long 2018-07-12  324  		cnt = __neg_dentry_nfree_dec(*pcnt - neg_dentry_percpu_limit);
bcc9ba8b Waiman Long 2018-07-12  325  		*pcnt -= cnt;
bcc9ba8b Waiman Long 2018-07-12  326  	}
bcc9ba8b Waiman Long 2018-07-12  327  	put_cpu_ptr(&nr_dentry_neg);
bcc9ba8b Waiman Long 2018-07-12  328  
2aa8bf46 Waiman Long 2018-07-12  329  	if (cnt)
2aa8bf46 Waiman Long 2018-07-12  330  		goto out;
2aa8bf46 Waiman Long 2018-07-12  331  
bcc9ba8b Waiman Long 2018-07-12  332  	/*
2aa8bf46 Waiman Long 2018-07-12  333  	 * Put out a warning every minute or so if there are just too many
2aa8bf46 Waiman Long 2018-07-12  334  	 * negative dentries.
bcc9ba8b Waiman Long 2018-07-12  335  	 */
2aa8bf46 Waiman Long 2018-07-12  336  	current_time = jiffies;
bcc9ba8b Waiman Long 2018-07-12  337  
2aa8bf46 Waiman Long 2018-07-12  338  	if (current_time < ndblk.warn_jiffies + NEG_WARN_PERIOD)
2aa8bf46 Waiman Long 2018-07-12  339  		goto out;
2aa8bf46 Waiman Long 2018-07-12  340  	/*
2aa8bf46 Waiman Long 2018-07-12  341  	 * Update the time in ndblk.warn_jiffies and print a warning
2aa8bf46 Waiman Long 2018-07-12  342  	 * if time update is successful.
2aa8bf46 Waiman Long 2018-07-12  343  	 */
2aa8bf46 Waiman Long 2018-07-12  344  	raw_spin_lock(&ndblk.nfree_lock);
2aa8bf46 Waiman Long 2018-07-12  345  	if (current_time < ndblk.warn_jiffies + NEG_WARN_PERIOD) {
2aa8bf46 Waiman Long 2018-07-12  346  		raw_spin_unlock(&ndblk.nfree_lock);
2aa8bf46 Waiman Long 2018-07-12  347  		goto out;
2aa8bf46 Waiman Long 2018-07-12  348  	}
2aa8bf46 Waiman Long 2018-07-12  349  	ndblk.warn_jiffies = current_time;
2aa8bf46 Waiman Long 2018-07-12  350  	raw_spin_unlock(&ndblk.nfree_lock);
2aa8bf46 Waiman Long 2018-07-12  351  
2aa8bf46 Waiman Long 2018-07-12  352  	/*
2aa8bf46 Waiman Long 2018-07-12  353  	 * Get the current negative dentry count & print a warning.
2aa8bf46 Waiman Long 2018-07-12  354  	 */
2aa8bf46 Waiman Long 2018-07-12 @355  	cnt = get_nr_dentry_neg();
2aa8bf46 Waiman Long 2018-07-12  356  	pr_warn("Warning: Too many negative dentries (%ld). "
2aa8bf46 Waiman Long 2018-07-12  357  		"This warning can be disabled by writing 0 to \"fs/neg-dentry-limit\" or increasing the limit.\n",
2aa8bf46 Waiman Long 2018-07-12  358  		cnt);
2aa8bf46 Waiman Long 2018-07-12  359  out:
2aa8bf46 Waiman Long 2018-07-12  360  	return;
bcc9ba8b Waiman Long 2018-07-12  361  }
bcc9ba8b Waiman Long 2018-07-12  362  

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 6369 bytes --]

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v7 6/6] fs/dcache: Allow deconfiguration of negative dentry code to reduce kernel size
  2018-07-12 16:46 ` [PATCH v7 6/6] fs/dcache: Allow deconfiguration of negative dentry code to reduce kernel size Waiman Long
@ 2018-07-17  1:25   ` kbuild test robot
  0 siblings, 0 replies; 11+ messages in thread
From: kbuild test robot @ 2018-07-17  1:25 UTC (permalink / raw)
  To: Waiman Long
  Cc: kbuild-all, Alexander Viro, Jonathan Corbet, Luis R. Rodriguez,
	Kees Cook, linux-kernel, linux-fsdevel, linux-mm, linux-doc,
	Linus Torvalds, Jan Kara, Paul E. McKenney, Andrew Morton,
	Ingo Molnar, Miklos Szeredi, Matthew Wilcox, Larry Woodman,
	James Bottomley, Wangkai (Kevin C),
	Michal Hocko, Waiman Long

[-- Attachment #1: Type: text/plain, Size: 6472 bytes --]

Hi Waiman,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on linus/master]
[also build test ERROR on v4.18-rc4]
[cannot apply to next-20180713]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Waiman-Long/fs-dcache-Track-report-number-of-negative-dentries/20180714-161258
config: h8300-h8300h-sim_defconfig (attached as .config)
compiler: h8300-linux-gcc (GCC) 8.1.0
reproduce:
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        GCC_VERSION=8.1.0 make.cross ARCH=h8300 
:::::: branch date: 65 minutes ago
:::::: commit date: 65 minutes ago

All errors (new ones prefixed by >>):

   fs/dcache.c: In function 'neg_dentry_inc_slowpath':
>> fs/dcache.c:374:8: error: implicit declaration of function 'get_nr_dentry_neg'; did you mean 'get_nr_dirty_inodes'? [-Werror=implicit-function-declaration]
     cnt = get_nr_dentry_neg();
           ^~~~~~~~~~~~~~~~~
           get_nr_dirty_inodes
   cc1: some warnings being treated as errors

# https://github.com/0day-ci/linux/commit/ca68ee513a450445b269248c2859302c8931a294
git remote add linux-review https://github.com/0day-ci/linux
git remote update linux-review
git checkout ca68ee513a450445b269248c2859302c8931a294
vim +374 fs/dcache.c

bcc9ba8b Waiman Long 2018-07-12  311  
2ccdd02c Waiman Long 2018-07-12  312  static noinline int neg_dentry_inc_slowpath(struct dentry *dentry)
bcc9ba8b Waiman Long 2018-07-12  313  {
bcc9ba8b Waiman Long 2018-07-12  314  	long cnt = 0, *pcnt;
2aa8bf46 Waiman Long 2018-07-12  315  	unsigned long current_time;
bcc9ba8b Waiman Long 2018-07-12  316  
bcc9ba8b Waiman Long 2018-07-12  317  	/*
bcc9ba8b Waiman Long 2018-07-12  318  	 * Try to move some negative dentry quota from the global free
bcc9ba8b Waiman Long 2018-07-12  319  	 * pool to the percpu count to allow more negative dentries to
bcc9ba8b Waiman Long 2018-07-12  320  	 * be added to the LRU.
bcc9ba8b Waiman Long 2018-07-12  321  	 */
bcc9ba8b Waiman Long 2018-07-12  322  	pcnt = get_cpu_ptr(&nr_dentry_neg);
bcc9ba8b Waiman Long 2018-07-12  323  	if ((READ_ONCE(ndblk.nfree) > 0) &&
bcc9ba8b Waiman Long 2018-07-12  324  	    (*pcnt > neg_dentry_percpu_limit)) {
bcc9ba8b Waiman Long 2018-07-12  325  		cnt = __neg_dentry_nfree_dec(*pcnt - neg_dentry_percpu_limit);
bcc9ba8b Waiman Long 2018-07-12  326  		*pcnt -= cnt;
bcc9ba8b Waiman Long 2018-07-12  327  	}
bcc9ba8b Waiman Long 2018-07-12  328  	put_cpu_ptr(&nr_dentry_neg);
bcc9ba8b Waiman Long 2018-07-12  329  
2aa8bf46 Waiman Long 2018-07-12  330  	if (cnt)
2aa8bf46 Waiman Long 2018-07-12  331  		goto out;
2aa8bf46 Waiman Long 2018-07-12  332  
bcc9ba8b Waiman Long 2018-07-12  333  	/*
2ccdd02c Waiman Long 2018-07-12  334  	 * Kill the dentry by setting the DCACHE_KILL_NEGATIVE flag and
2ccdd02c Waiman Long 2018-07-12  335  	 * dec the negative dentry count if the enforcing option is on.
2ccdd02c Waiman Long 2018-07-12  336  	 */
2ccdd02c Waiman Long 2018-07-12  337  	if (neg_dentry_enforce) {
2ccdd02c Waiman Long 2018-07-12  338  		dentry->d_flags |= DCACHE_KILL_NEGATIVE;
2ccdd02c Waiman Long 2018-07-12  339  		this_cpu_dec(nr_dentry_neg);
2ccdd02c Waiman Long 2018-07-12  340  
2ccdd02c Waiman Long 2018-07-12  341  		/*
2ccdd02c Waiman Long 2018-07-12  342  		 * When the dentry is not put into the LRU, we
2ccdd02c Waiman Long 2018-07-12  343  		 * need to keep the reference count to 1 to
2ccdd02c Waiman Long 2018-07-12  344  		 * avoid problem when killing it.
2ccdd02c Waiman Long 2018-07-12  345  		 */
2ccdd02c Waiman Long 2018-07-12  346  		WARN_ON_ONCE(dentry->d_lockref.count);
2ccdd02c Waiman Long 2018-07-12  347  		dentry->d_lockref.count = 1;
2ccdd02c Waiman Long 2018-07-12  348  		return -1; /* Kill the dentry now */
2ccdd02c Waiman Long 2018-07-12  349  	}
2ccdd02c Waiman Long 2018-07-12  350  
2ccdd02c Waiman Long 2018-07-12  351  	/*
2aa8bf46 Waiman Long 2018-07-12  352  	 * Put out a warning every minute or so if there are just too many
2aa8bf46 Waiman Long 2018-07-12  353  	 * negative dentries.
bcc9ba8b Waiman Long 2018-07-12  354  	 */
2aa8bf46 Waiman Long 2018-07-12  355  	current_time = jiffies;
bcc9ba8b Waiman Long 2018-07-12  356  
2aa8bf46 Waiman Long 2018-07-12  357  	if (current_time < ndblk.warn_jiffies + NEG_WARN_PERIOD)
2aa8bf46 Waiman Long 2018-07-12  358  		goto out;
2aa8bf46 Waiman Long 2018-07-12  359  	/*
2aa8bf46 Waiman Long 2018-07-12  360  	 * Update the time in ndblk.warn_jiffies and print a warning
2aa8bf46 Waiman Long 2018-07-12  361  	 * if time update is successful.
2aa8bf46 Waiman Long 2018-07-12  362  	 */
2aa8bf46 Waiman Long 2018-07-12  363  	raw_spin_lock(&ndblk.nfree_lock);
2aa8bf46 Waiman Long 2018-07-12  364  	if (current_time < ndblk.warn_jiffies + NEG_WARN_PERIOD) {
2aa8bf46 Waiman Long 2018-07-12  365  		raw_spin_unlock(&ndblk.nfree_lock);
2aa8bf46 Waiman Long 2018-07-12  366  		goto out;
2aa8bf46 Waiman Long 2018-07-12  367  	}
2aa8bf46 Waiman Long 2018-07-12  368  	ndblk.warn_jiffies = current_time;
2aa8bf46 Waiman Long 2018-07-12  369  	raw_spin_unlock(&ndblk.nfree_lock);
2aa8bf46 Waiman Long 2018-07-12  370  
2aa8bf46 Waiman Long 2018-07-12  371  	/*
2aa8bf46 Waiman Long 2018-07-12  372  	 * Get the current negative dentry count & print a warning.
2aa8bf46 Waiman Long 2018-07-12  373  	 */
2aa8bf46 Waiman Long 2018-07-12 @374  	cnt = get_nr_dentry_neg();
2aa8bf46 Waiman Long 2018-07-12  375  	pr_warn("Warning: Too many negative dentries (%ld). "
2ccdd02c Waiman Long 2018-07-12  376  		"This warning can be disabled by writing 0 to \"fs/neg-dentry-limit\", increasing the limit or writing 1 to \"fs/neg-dentry-enforce\".\n",
2aa8bf46 Waiman Long 2018-07-12  377  		cnt);
2aa8bf46 Waiman Long 2018-07-12  378  out:
2ccdd02c Waiman Long 2018-07-12  379  	return 0;
bcc9ba8b Waiman Long 2018-07-12  380  }
bcc9ba8b Waiman Long 2018-07-12  381  

:::::: The code at line 374 was first introduced by commit
:::::: 2aa8bf4658af0dbc07ae9ea07d04937a347e3ef4 fs/dcache: Print negative dentry warning every min until turned off by user

:::::: TO: Waiman Long <longman@redhat.com>
:::::: CC: 0day robot <lkp@intel.com>

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 4383 bytes --]

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2018-07-17  1:25 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-07-12 16:45 [PATCH v7 0/6] fs/dcache: Track & limit # of negative dentries Waiman Long
2018-07-12 16:46 ` [PATCH v7 1/6] fs/dcache: Track & report number " Waiman Long
2018-07-12 16:46 ` [PATCH v7 2/6] fs/dcache: Add negative dentries to LRU head initially Waiman Long
2018-07-12 16:46 ` [PATCH v7 3/6] fs/dcache: Add sysctl parameter neg-dentry-limit as a soft limit on negative dentries Waiman Long
2018-07-12 16:56   ` Matthew Wilcox
2018-07-12 17:04     ` Waiman Long
2018-07-12 16:46 ` [PATCH v7 4/6] fs/dcache: Print negative dentry warning every min until turned off by user Waiman Long
2018-07-17  1:24   ` kbuild test robot
2018-07-12 16:46 ` [PATCH v7 5/6] fs/dcache: Allow optional enforcement of negative dentry limit Waiman Long
2018-07-12 16:46 ` [PATCH v7 6/6] fs/dcache: Allow deconfiguration of negative dentry code to reduce kernel size Waiman Long
2018-07-17  1:25   ` kbuild test robot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).