mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	mm: protect free_pgtables with mmap_lock write lock in exit_mmap
oom-reaper and process_mrelease system call should protect against races with exit_mmap which can destroy page tables while they walk the VMA tree. oom-reaper protects from that race by setting MMF_OOM_VICTIM and by relying on exit_mmap to set MMF_OOM_SKIP before taking and releasing mmap_write_lock. process_mrelease has to elevate mm->mm_users to prevent such race. Both oom-reaper and process_mrelease hold mmap_read_lock when walking the VMA tree. The locking rules and mechanisms could be simpler if exit_mmap takes mmap_write_lock while executing destructive operations such as free_pgtables. Change exit_mmap to hold the mmap_write_lock when calling unlock_range, free_pgtables and remove_vma. Note also that because oom-reaper checks VM_LOCKED flag, unlock_range() should not be allowed to race with it. Before this patch, remove_vma used to be called with no locks held, however with fput being executed asynchronously and vm_ops->close not being allowed to hold mmap_lock (it is called from __split_vma with mmap_sem held for write), changing that should be fine. In most cases this lock should be uncontended. Previously, Kirill reported ~4% regression caused by a similar change [1]. We reran the same test and although the individual results are quite noisy, the percentiles show lower regression with 1.6% being the worst case [2]. The change allows oom-reaper and process_mrelease to execute safely under mmap_read_lock without worries that exit_mmap might destroy page tables from under them. [1] https://lore.kernel.org/all/20170725141723.ivukwhddk2voyhuc@node.shutemov.name/ [2] https://lore.kernel.org/all/CAJuCfpGC9-c9P40x7oy=jy5SphMcd0o0G_6U1-+JAziGKG6dGA@mail.gmail.com/ Link: https://lkml.kernel.org/r/20211209191325.3069345-1-surenb@google.com Signed-off-by: Suren Baghdasaryan <surenb@google.com> Acked-by: Michal Hocko <mhocko@suse.com> Cc: David Rientjes <rientjes@google.com> Cc: Matthew Wilcox <willy@infradead.org> Cc: Johannes Weiner <hannes@cmpxchg.org> Cc: Roman Gushchin <guro@fb.com> Cc: Rik van Riel <riel@surriel.com> Cc: Minchan Kim <minchan@kernel.org> Cc: Kirill A. Shutemov <kirill@shutemov.name> Cc: Andrea Arcangeli <aarcange@redhat.com> Cc: Christian Brauner <christian@brauner.io> Cc: Christoph Hellwig <hch@infradead.org> Cc: Oleg Nesterov <oleg@redhat.com> Cc: David Hildenbrand <david@redhat.com> Cc: Jann Horn <jannh@google.com> Cc: Shakeel Butt <shakeelb@google.com> Cc: Andy Lutomirski <luto@kernel.org> Cc: Christian Brauner <christian.brauner@ubuntu.com> Cc: Florian Weimer <fweimer@redhat.com> Cc: Jan Engelhardt <jengelh@inai.de> Cc: Tim Murray <timmurray@google.com> Cc: Jason Gunthorpe <jgg@nvidia.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
		
							parent
							
								
									36090def7b
								
							
						
					
					
						commit
						64591e8605
					
				
					 1 changed files with 8 additions and 8 deletions
				
			
		
							
								
								
									
										16
									
								
								mm/mmap.c
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								mm/mmap.c
									
									
									
									
									
								
							| 
						 | 
					@ -3149,25 +3149,27 @@ void exit_mmap(struct mm_struct *mm)
 | 
				
			||||||
		 * to mmu_notifier_release(mm) ensures mmu notifier callbacks in
 | 
							 * to mmu_notifier_release(mm) ensures mmu notifier callbacks in
 | 
				
			||||||
		 * __oom_reap_task_mm() will not block.
 | 
							 * __oom_reap_task_mm() will not block.
 | 
				
			||||||
		 *
 | 
							 *
 | 
				
			||||||
		 * This needs to be done before calling munlock_vma_pages_all(),
 | 
							 * This needs to be done before calling unlock_range(),
 | 
				
			||||||
		 * which clears VM_LOCKED, otherwise the oom reaper cannot
 | 
							 * which clears VM_LOCKED, otherwise the oom reaper cannot
 | 
				
			||||||
		 * reliably test it.
 | 
							 * reliably test it.
 | 
				
			||||||
		 */
 | 
							 */
 | 
				
			||||||
		(void)__oom_reap_task_mm(mm);
 | 
							(void)__oom_reap_task_mm(mm);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		set_bit(MMF_OOM_SKIP, &mm->flags);
 | 
							set_bit(MMF_OOM_SKIP, &mm->flags);
 | 
				
			||||||
		mmap_write_lock(mm);
 | 
					 | 
				
			||||||
		mmap_write_unlock(mm);
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mmap_write_lock(mm);
 | 
				
			||||||
	if (mm->locked_vm)
 | 
						if (mm->locked_vm)
 | 
				
			||||||
		unlock_range(mm->mmap, ULONG_MAX);
 | 
							unlock_range(mm->mmap, ULONG_MAX);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	arch_exit_mmap(mm);
 | 
						arch_exit_mmap(mm);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	vma = mm->mmap;
 | 
						vma = mm->mmap;
 | 
				
			||||||
	if (!vma)	/* Can happen if dup_mmap() received an OOM */
 | 
						if (!vma) {
 | 
				
			||||||
 | 
							/* Can happen if dup_mmap() received an OOM */
 | 
				
			||||||
 | 
							mmap_write_unlock(mm);
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	lru_add_drain();
 | 
						lru_add_drain();
 | 
				
			||||||
	flush_cache_mm(mm);
 | 
						flush_cache_mm(mm);
 | 
				
			||||||
| 
						 | 
					@ -3178,16 +3180,14 @@ void exit_mmap(struct mm_struct *mm)
 | 
				
			||||||
	free_pgtables(&tlb, vma, FIRST_USER_ADDRESS, USER_PGTABLES_CEILING);
 | 
						free_pgtables(&tlb, vma, FIRST_USER_ADDRESS, USER_PGTABLES_CEILING);
 | 
				
			||||||
	tlb_finish_mmu(&tlb);
 | 
						tlb_finish_mmu(&tlb);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/* Walk the list again, actually closing and freeing it. */
 | 
				
			||||||
	 * Walk the list again, actually closing and freeing it,
 | 
					 | 
				
			||||||
	 * with preemption enabled, without holding any MM locks.
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	while (vma) {
 | 
						while (vma) {
 | 
				
			||||||
		if (vma->vm_flags & VM_ACCOUNT)
 | 
							if (vma->vm_flags & VM_ACCOUNT)
 | 
				
			||||||
			nr_accounted += vma_pages(vma);
 | 
								nr_accounted += vma_pages(vma);
 | 
				
			||||||
		vma = remove_vma(vma);
 | 
							vma = remove_vma(vma);
 | 
				
			||||||
		cond_resched();
 | 
							cond_resched();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						mmap_write_unlock(mm);
 | 
				
			||||||
	vm_unacct_memory(nr_accounted);
 | 
						vm_unacct_memory(nr_accounted);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue