hulk inclusion category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/I8WPQW CVE: NA
--------------------------------
Restrictions of negative dentry can avoid softlock when too many dentry in memory, but restrictions before do not always take effect. For example, removing files which has been created would not enter retain_dentry(), because the dentry of file maybe has been added to lru list of superblock, it caused that fast_dput() reutrn true in last dput().
So, add restriction logic for the file which has been added to the lru list in fast_dput(), it prevents the negative dentry exceeding the limit when deleting existing file. Factor out negative dentry limit logic into limit_negative_dentry(), make the code more clean.
Fixes: 7ba5d5d0e8b5 ("fs/dcache.c: avoid softlock since too many negative dentry") Signed-off-by: Long Li leo.lilong@huawei.com --- fs/dcache.c | 58 +++++++++++++++++++++++++++++++++-------------------- 1 file changed, 36 insertions(+), 22 deletions(-)
diff --git a/fs/dcache.c b/fs/dcache.c index ef1641398eda..e52e3abdf378 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -636,10 +636,36 @@ static inline struct dentry *lock_parent(struct dentry *dentry) return __lock_parent(dentry); }
-static inline bool retain_dentry(struct dentry *dentry) +/* + * Return true if dentry is negative and exceed negative dentry limit. + */ +static inline bool limit_negative_dentry(struct dentry *dentry) { struct dentry *parent;
+ parent = dentry->d_parent; + if (unlikely(!parent)) + return false; + + 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 (atomic_read(&parent->d_neg_dnum) >= NEG_DENTRY_LIMIT) + return true; + } + + return false; +} + +static inline bool retain_dentry(struct dentry *dentry) +{ WARN_ON(d_in_lookup(dentry));
/* Unreachable? Get rid of it */ @@ -654,27 +680,9 @@ static inline bool retain_dentry(struct dentry *dentry) 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 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) + if (unlikely(limit_negative_dentry(dentry))) return false;
-noparent: /* retain; LRU fodder */ dentry->d_lockref.count--; if (unlikely(!(dentry->d_flags & DCACHE_LRU_LIST))) @@ -808,8 +816,14 @@ static inline bool fast_dput(struct dentry *dentry) d_flags &= DCACHE_REFERENCED | DCACHE_LRU_LIST | DCACHE_DISCONNECTED;
/* Nothing to do? Dropping the reference was all we needed? */ - if (d_flags == (DCACHE_REFERENCED | DCACHE_LRU_LIST) && !d_unhashed(dentry)) - return true; + if (d_flags == (DCACHE_REFERENCED | DCACHE_LRU_LIST) && !d_unhashed(dentry)) { + /* + * If dentry is negative and need to be limitted, it maybe need + * to be killed, so shouldn't fast put and retain in memory. + */ + if (likely(!limit_negative_dentry(dentry))) + return true; + }
/* * Not the fast normal case? Get the lock. We've already decremented
反馈: 您发送到kernel@openeuler.org的补丁/补丁集,已成功转换为PR! PR链接地址: https://gitee.com/openeuler/kernel/pulls/4162 邮件列表地址:https://mailweb.openeuler.org/hyperkitty/list/kernel@openeuler.org/message/O...
FeedBack: The patch(es) which you have sent to kernel@openeuler.org mailing list has been converted to a pull request successfully! Pull request link: https://gitee.com/openeuler/kernel/pulls/4162 Mailing list address: https://mailweb.openeuler.org/hyperkitty/list/kernel@openeuler.org/message/O...