fix #1633: potential deadlock with shmem
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
This commit is contained in:
		
							parent
							
								
									498bdfe5e5
								
							
						
					
					
						commit
						b25749a58c
					
				| @ -0,0 +1,103 @@ | ||||
| From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 | ||||
| From: "Kirill A. Shutemov" <kirill.shutemov@linux.intel.com> | ||||
| Date: Fri, 23 Mar 2018 09:19:21 +0100 | ||||
| Subject: [PATCH] mm/shmem: do not wait for lock_page() in | ||||
|  shmem_unused_huge_shrink() | ||||
| MIME-Version: 1.0 | ||||
| Content-Type: text/plain; charset=UTF-8 | ||||
| Content-Transfer-Encoding: 8bit | ||||
| 
 | ||||
| shmem_unused_huge_shrink() gets called from reclaim path.  Waiting for | ||||
| page lock may lead to deadlock there. | ||||
| 
 | ||||
| There was a bug report that may be attributed to this: | ||||
| 
 | ||||
| http://lkml.kernel.org/r/alpine.LRH.2.11.1801242349220.30642@mail.ewheeler.net | ||||
| 
 | ||||
| Replace lock_page() with trylock_page() and skip the page if we failed to | ||||
| lock it.  We will get to the page on the next scan. | ||||
| 
 | ||||
| We can test for the PageTransHuge() outside the page lock as we only need | ||||
| protection against splitting the page under us.  Holding pin oni the page | ||||
| is enough for this. | ||||
| 
 | ||||
| Link: http://lkml.kernel.org/r/20180316210830.43738-1-kirill.shutemov@linux.intel.com | ||||
| Fixes: 779750d20b93 ("shmem: split huge pages beyond i_size under memory pressure") | ||||
| Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com> | ||||
| Reported-by: Eric Wheeler <linux-mm@lists.ewheeler.net> | ||||
| Acked-by: Michal Hocko <mhocko@suse.com> | ||||
| Reviewed-by: Andrew Morton <akpm@linux-foundation.org> | ||||
| Cc: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> | ||||
| Cc: Hugh Dickins <hughd@google.com> | ||||
| Cc: <stable@vger.kernel.org>	[4.8+] | ||||
| Signed-off-by: Andrew Morton <> | ||||
| (cherry-picked from https://git.kernel.org/pub/scm/linux/kernel/git/mhocko/mm.git/commit/?h=since-4.15&id=73eccc61c701ee7b4223aea2079542a712feeea7) | ||||
| Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com> | ||||
| ---
 | ||||
|  mm/shmem.c | 31 ++++++++++++++++++++----------- | ||||
|  1 file changed, 20 insertions(+), 11 deletions(-) | ||||
| 
 | ||||
| diff --git a/mm/shmem.c b/mm/shmem.c
 | ||||
| index f6695c111086..800482fe6ed6 100644
 | ||||
| --- a/mm/shmem.c
 | ||||
| +++ b/mm/shmem.c
 | ||||
| @@ -497,36 +497,45 @@ 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; | ||||
|   | ||||
| +		/* No huge page at the end of the file: nothing to split */
 | ||||
|  		if (!PageTransHuge(page)) { | ||||
| -			unlock_page(page);
 | ||||
|  			put_page(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;
 | ||||
| +		}
 | ||||
| +
 | ||||
|  		ret = split_huge_page(page); | ||||
|  		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); | ||||
|  	} | ||||
|   | ||||
| -- 
 | ||||
| 2.14.2 | ||||
| 
 | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Fabian Grünbichler
						Fabian Grünbichler