Re: [PATCH] mm/shmem: Do not wait for lock_page() in shmem_unused_huge_shrink()
From: Tetsuo Handa
Date: Fri Mar 16 2018 - 10:42:30 EST
Kirill A. Shutemov wrote:
> On Fri, Mar 16, 2018 at 10:14:24PM +0900, Tetsuo Handa wrote:
> > f2fs is doing
> >
> > page = f2fs_pagecache_get_page(inode->i_mapping, 0, FGP_LOCK|FGP_NOWAIT, 0);
> >
> > which calls
> >
> > struct page *pagecache_get_page(inode->i_mapping, 0, FGP_LOCK|FGP_NOWAIT, 0);
> >
> > . Then, can't we define
> >
> > static inline struct page *find_trylock_page(struct address_space *mapping,
> > pgoff_t offset)
> > {
> > return pagecache_get_page(mapping, offset, FGP_LOCK|FGP_NOWAIT, 0);
> > }
> >
> > and replace find_lock_page() with find_trylock_page() ?
>
> This won't work in this case. We need to destinct no-page-in-page-cache
> from failed-to-lock-page. We take different routes depending on this.
>
OK. Then, I think we should avoid reordering trylock_page() and PageTransHuge()
without patch description why it is safe. Below patch preserves the ordering
and sounds safer for stable. But either patch, please add why it is safe to omit
"/* Has the page been truncated? */" check which would have been done for FGP_LOCK
in patch description.
---
mm/shmem.c | 30 ++++++++++++++++++++----------
1 file changed, 20 insertions(+), 10 deletions(-)
diff --git a/mm/shmem.c b/mm/shmem.c
index 8ead6cb..5e94ca4 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -493,16 +493,27 @@ static unsigned long shmem_unused_huge_shrink(struct shmem_sb_info *sbinfo,
info = list_entry(pos, struct shmem_inode_info, shrinklist);
inode = &info->vfs_inode;
- if (nr_to_split && split >= nr_to_split) {
- iput(inode);
- continue;
- }
+ if (nr_to_split && split >= nr_to_split)
+ goto leave;
- page = find_lock_page(inode->i_mapping,
+ page = find_get_page(inode->i_mapping,
(inode->i_size & HPAGE_PMD_MASK) >> PAGE_SHIFT);
if (!page)
goto drop;
+ /*
+ * Leave the inode on the list if we failed to lock
+ * the page at this time.
+ *
+ * Waiting for the lock may lead to deadlock in the
+ * reclaim path.
+ */
+ if (!trylock_page(page)) {
+ put_page(page);
+ goto leave;
+ }
+
+ /* No huge page at the end of the file: nothing to split */
if (!PageTransHuge(page)) {
unlock_page(page);
put_page(page);
@@ -513,16 +524,15 @@ static unsigned long shmem_unused_huge_shrink(struct shmem_sb_info *sbinfo,
unlock_page(page);
put_page(page);
- if (ret) {
- /* split failed: leave it on the list */
- iput(inode);
- continue;
- }
+ /* If split failed leave the inode on the list */
+ if (ret)
+ goto leave;
split++;
drop:
list_del_init(&info->shrinklist);
removed++;
+leave:
iput(inode);
}
--