提交 c44e1f78 编写于 作者: Y yangerkun 提交者: Zheng Zengkai

fs/dcache.c: avoid softlock since too many negative dentry

hulk inclusion
category: bugfix
bugzilla: 185805, https://gitee.com/openeuler/kernel/issues/I4JX0L
CVE: NA

---------------------------

Parallel thread to add negative dentry under root dir. Sometimes later,
'systemctl daemon-reload' will report softlockup since
__fsnotify_update_child_dentry_flags need update all child under root
dentry without distinguish does it active or not. It will waste so long
time with catching d_lock of root dentry. And other thread try to
spin_lock d_lock will run overtime.

Limit negative dentry under dir can avoid this.
Signed-off-by: Nyangerkun <yangerkun@huawei.com>
Reviewed-by: NMiao Xie <miaoxie@huawei.com>
Signed-off-by: NYang Yingliang <yangyingliang@huawei.com>

Conflicts:
	fs/dcache.c
	include/linux/dcache.h
Reviewed-by: NZhang Yi <yi.zhang@huawei.com>
Signed-off-by: NZheng Zengkai <zhengzengkai@huawei.com>
上级 6a8c6793
...@@ -314,10 +314,18 @@ static inline void __d_set_inode_and_type(struct dentry *dentry, ...@@ -314,10 +314,18 @@ static inline void __d_set_inode_and_type(struct dentry *dentry,
unsigned type_flags) unsigned type_flags)
{ {
unsigned flags; unsigned flags;
struct dentry *parent;
parent = dentry->d_parent;
if ((dentry->d_flags & DCACHE_NEGATIVE_ACCOUNT) && parent) {
WARN_ON(!inode);
atomic_dec(&parent->d_neg_dnum);
}
dentry->d_inode = inode; dentry->d_inode = inode;
flags = READ_ONCE(dentry->d_flags); flags = READ_ONCE(dentry->d_flags);
flags &= ~(DCACHE_ENTRY_TYPE | DCACHE_FALLTHRU); flags &= ~(DCACHE_ENTRY_TYPE | DCACHE_FALLTHRU |
DCACHE_NEGATIVE_ACCOUNT);
flags |= type_flags; flags |= type_flags;
smp_store_release(&dentry->d_flags, flags); smp_store_release(&dentry->d_flags, flags);
} }
...@@ -336,6 +344,7 @@ static inline void __d_clear_type_and_inode(struct dentry *dentry) ...@@ -336,6 +344,7 @@ static inline void __d_clear_type_and_inode(struct dentry *dentry)
static void dentry_free(struct dentry *dentry) static void dentry_free(struct dentry *dentry)
{ {
WARN_ON(!hlist_unhashed(&dentry->d_u.d_alias)); WARN_ON(!hlist_unhashed(&dentry->d_u.d_alias));
WARN_ON(dentry->d_flags & DCACHE_NEGATIVE_ACCOUNT);
if (unlikely(dname_external(dentry))) { if (unlikely(dname_external(dentry))) {
struct external_name *p = external_name(dentry); struct external_name *p = external_name(dentry);
if (likely(atomic_dec_and_test(&p->u.count))) { if (likely(atomic_dec_and_test(&p->u.count))) {
...@@ -573,8 +582,14 @@ static void __dentry_kill(struct dentry *dentry) ...@@ -573,8 +582,14 @@ static void __dentry_kill(struct dentry *dentry)
/* if it was on the hash then remove it */ /* if it was on the hash then remove it */
__d_drop(dentry); __d_drop(dentry);
dentry_unlist(dentry, parent); dentry_unlist(dentry, parent);
if (parent) if (parent) {
if (dentry->d_flags & DCACHE_NEGATIVE_ACCOUNT) {
atomic_dec(&parent->d_neg_dnum);
dentry->d_flags &= ~DCACHE_NEGATIVE_ACCOUNT;
}
spin_unlock(&parent->d_lock); spin_unlock(&parent->d_lock);
}
if (dentry->d_inode) if (dentry->d_inode)
dentry_unlink_inode(dentry); dentry_unlink_inode(dentry);
else else
...@@ -634,6 +649,8 @@ static inline struct dentry *lock_parent(struct dentry *dentry) ...@@ -634,6 +649,8 @@ static inline struct dentry *lock_parent(struct dentry *dentry)
static inline bool retain_dentry(struct dentry *dentry) static inline bool retain_dentry(struct dentry *dentry)
{ {
struct dentry *parent;
WARN_ON(d_in_lookup(dentry)); WARN_ON(d_in_lookup(dentry));
/* Unreachable? Get rid of it */ /* Unreachable? Get rid of it */
...@@ -651,6 +668,27 @@ static inline bool retain_dentry(struct dentry *dentry) ...@@ -651,6 +668,27 @@ static inline bool retain_dentry(struct dentry *dentry)
if (unlikely(dentry->d_flags & DCACHE_DONTCACHE)) if (unlikely(dentry->d_flags & DCACHE_DONTCACHE))
return false; return false;
if (unlikely(!dentry->d_parent))
goto noparent;
parent = dentry->d_parent;
/* Return false if it's negative */
WARN_ON((atomic_read(&parent->d_neg_dnum) < 0));
if (!dentry->d_inode) {
if (!(dentry->d_flags & DCACHE_NEGATIVE_ACCOUNT)) {
unsigned int flags = READ_ONCE(dentry->d_flags);
flags |= DCACHE_NEGATIVE_ACCOUNT;
WRITE_ONCE(dentry->d_flags, flags);
atomic_inc(&parent->d_neg_dnum);
}
}
if (!dentry->d_inode &&
atomic_read(&parent->d_neg_dnum) >= NEG_DENTRY_LIMIT)
return false;
noparent:
/* retain; LRU fodder */ /* retain; LRU fodder */
dentry->d_lockref.count--; dentry->d_lockref.count--;
if (unlikely(!(dentry->d_flags & DCACHE_LRU_LIST))) if (unlikely(!(dentry->d_flags & DCACHE_LRU_LIST)))
...@@ -1749,6 +1787,7 @@ static struct dentry *__d_alloc(struct super_block *sb, const struct qstr *name) ...@@ -1749,6 +1787,7 @@ static struct dentry *__d_alloc(struct super_block *sb, const struct qstr *name)
seqcount_spinlock_init(&dentry->d_seq, &dentry->d_lock); seqcount_spinlock_init(&dentry->d_seq, &dentry->d_lock);
dentry->d_inode = NULL; dentry->d_inode = NULL;
dentry->d_parent = dentry; dentry->d_parent = dentry;
atomic_set(&dentry->d_neg_dnum, 0);
dentry->d_sb = sb; dentry->d_sb = sb;
dentry->d_op = NULL; dentry->d_op = NULL;
dentry->d_fsdata = NULL; dentry->d_fsdata = NULL;
......
...@@ -84,6 +84,7 @@ extern struct dentry_stat_t dentry_stat; ...@@ -84,6 +84,7 @@ extern struct dentry_stat_t dentry_stat;
# endif # endif
#endif #endif
#define NEG_DENTRY_LIMIT 16384
#define d_lock d_lockref.lock #define d_lock d_lockref.lock
struct dentry { struct dentry {
...@@ -118,6 +119,8 @@ struct dentry { ...@@ -118,6 +119,8 @@ struct dentry {
struct hlist_bl_node d_in_lookup_hash; /* only for in-lookup ones */ struct hlist_bl_node d_in_lookup_hash; /* only for in-lookup ones */
struct rcu_head d_rcu; struct rcu_head d_rcu;
} d_u; } d_u;
/* negative dentry under this dentry, if it's dir */
atomic_t d_neg_dnum;
} __randomize_layout; } __randomize_layout;
/* /*
...@@ -219,6 +222,7 @@ struct dentry_operations { ...@@ -219,6 +222,7 @@ struct dentry_operations {
#define DCACHE_PAR_LOOKUP 0x10000000 /* being looked up (with parent locked shared) */ #define DCACHE_PAR_LOOKUP 0x10000000 /* being looked up (with parent locked shared) */
#define DCACHE_DENTRY_CURSOR 0x20000000 #define DCACHE_DENTRY_CURSOR 0x20000000
#define DCACHE_NORCU 0x40000000 /* No RCU delay for freeing */ #define DCACHE_NORCU 0x40000000 /* No RCU delay for freeing */
#define DCACHE_NEGATIVE_ACCOUNT 0x80000000
extern seqlock_t rename_lock; extern seqlock_t rename_lock;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册