aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRalph Campbell <rcampbell@nvidia.com>2018-10-30 18:04:14 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2018-10-31 11:54:11 -0400
commit86a2d59841ab0b147ffc1b7b3041af87927cf312 (patch)
treed10b1917669b769bfa3192228b05feec7f473e5f
parentaab8d0520e6e7c2a61f71195e6ce7007a4843afb (diff)
mm/hmm: fix race between hmm_mirror_unregister() and mmu_notifier callback
In hmm_mirror_unregister(), mm->hmm is set to NULL and then mmu_notifier_unregister_no_release() is called. That creates a small window where mmu_notifier can call mmu_notifier_ops with mm->hmm equal to NULL. Fix this by first unregistering mmu notifier callbacks and then setting mm->hmm to NULL. Similarly in hmm_register(), set mm->hmm before registering mmu_notifier callbacks so callback functions always see mm->hmm set. Link: http://lkml.kernel.org/r/20181019160442.18723-4-jglisse@redhat.com Signed-off-by: Ralph Campbell <rcampbell@nvidia.com> Signed-off-by: Jérôme Glisse <jglisse@redhat.com> Reviewed-by: John Hubbard <jhubbard@nvidia.com> Reviewed-by: Jérôme Glisse <jglisse@redhat.com> Reviewed-by: Balbir Singh <bsingharora@gmail.com> Cc: <stable@vger.kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--mm/hmm.c36
1 files changed, 21 insertions, 15 deletions
diff --git a/mm/hmm.c b/mm/hmm.c
index de9840f60100..63ca3b5be306 100644
--- a/mm/hmm.c
+++ b/mm/hmm.c
@@ -91,16 +91,6 @@ static struct hmm *hmm_register(struct mm_struct *mm)
91 spin_lock_init(&hmm->lock); 91 spin_lock_init(&hmm->lock);
92 hmm->mm = mm; 92 hmm->mm = mm;
93 93
94 /*
95 * We should only get here if hold the mmap_sem in write mode ie on
96 * registration of first mirror through hmm_mirror_register()
97 */
98 hmm->mmu_notifier.ops = &hmm_mmu_notifier_ops;
99 if (__mmu_notifier_register(&hmm->mmu_notifier, mm)) {
100 kfree(hmm);
101 return NULL;
102 }
103
104 spin_lock(&mm->page_table_lock); 94 spin_lock(&mm->page_table_lock);
105 if (!mm->hmm) 95 if (!mm->hmm)
106 mm->hmm = hmm; 96 mm->hmm = hmm;
@@ -108,12 +98,27 @@ static struct hmm *hmm_register(struct mm_struct *mm)
108 cleanup = true; 98 cleanup = true;
109 spin_unlock(&mm->page_table_lock); 99 spin_unlock(&mm->page_table_lock);
110 100
111 if (cleanup) { 101 if (cleanup)
112 mmu_notifier_unregister(&hmm->mmu_notifier, mm); 102 goto error;
113 kfree(hmm); 103
114 } 104 /*
105 * We should only get here if hold the mmap_sem in write mode ie on
106 * registration of first mirror through hmm_mirror_register()
107 */
108 hmm->mmu_notifier.ops = &hmm_mmu_notifier_ops;
109 if (__mmu_notifier_register(&hmm->mmu_notifier, mm))
110 goto error_mm;
115 111
116 return mm->hmm; 112 return mm->hmm;
113
114error_mm:
115 spin_lock(&mm->page_table_lock);
116 if (mm->hmm == hmm)
117 mm->hmm = NULL;
118 spin_unlock(&mm->page_table_lock);
119error:
120 kfree(hmm);
121 return NULL;
117} 122}
118 123
119void hmm_mm_destroy(struct mm_struct *mm) 124void hmm_mm_destroy(struct mm_struct *mm)
@@ -278,12 +283,13 @@ void hmm_mirror_unregister(struct hmm_mirror *mirror)
278 if (!should_unregister || mm == NULL) 283 if (!should_unregister || mm == NULL)
279 return; 284 return;
280 285
286 mmu_notifier_unregister_no_release(&hmm->mmu_notifier, mm);
287
281 spin_lock(&mm->page_table_lock); 288 spin_lock(&mm->page_table_lock);
282 if (mm->hmm == hmm) 289 if (mm->hmm == hmm)
283 mm->hmm = NULL; 290 mm->hmm = NULL;
284 spin_unlock(&mm->page_table_lock); 291 spin_unlock(&mm->page_table_lock);
285 292
286 mmu_notifier_unregister_no_release(&hmm->mmu_notifier, mm);
287 kfree(hmm); 293 kfree(hmm);
288} 294}
289EXPORT_SYMBOL(hmm_mirror_unregister); 295EXPORT_SYMBOL(hmm_mirror_unregister);