Skip to content

Commit e140151

Browse files
Ralph Campbelltorvalds
authored andcommitted
mm/hmm: HMM should have a callback before MM is destroyed
hmm_mirror_register() registers a callback for when the CPU pagetable is modified. Normally, the device driver will call hmm_mirror_unregister() when the process using the device is finished. However, if the process exits uncleanly, the struct_mm can be destroyed with no warning to the device driver. Link: http://lkml.kernel.org/r/[email protected] Signed-off-by: Ralph Campbell <[email protected]> Signed-off-by: Jérôme Glisse <[email protected]> Reviewed-by: John Hubbard <[email protected]> Cc: Evgeny Baskakov <[email protected]> Cc: Mark Hairgrove <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent b28b08d commit e140151

File tree

2 files changed

+38
-1
lines changed

2 files changed

+38
-1
lines changed

include/linux/hmm.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,16 @@ enum hmm_update_type {
218218
* @update: callback to update range on a device
219219
*/
220220
struct hmm_mirror_ops {
221+
/* release() - release hmm_mirror
222+
*
223+
* @mirror: pointer to struct hmm_mirror
224+
*
225+
* This is called when the mm_struct is being released.
226+
* The callback should make sure no references to the mirror occur
227+
* after the callback returns.
228+
*/
229+
void (*release)(struct hmm_mirror *mirror);
230+
221231
/* sync_cpu_device_pagetables() - synchronize page tables
222232
*
223233
* @mirror: pointer to struct hmm_mirror

mm/hmm.c

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,32 @@ static void hmm_invalidate_range(struct hmm *hmm,
160160
up_read(&hmm->mirrors_sem);
161161
}
162162

163+
static void hmm_release(struct mmu_notifier *mn, struct mm_struct *mm)
164+
{
165+
struct hmm_mirror *mirror;
166+
struct hmm *hmm = mm->hmm;
167+
168+
down_write(&hmm->mirrors_sem);
169+
mirror = list_first_entry_or_null(&hmm->mirrors, struct hmm_mirror,
170+
list);
171+
while (mirror) {
172+
list_del_init(&mirror->list);
173+
if (mirror->ops->release) {
174+
/*
175+
* Drop mirrors_sem so callback can wait on any pending
176+
* work that might itself trigger mmu_notifier callback
177+
* and thus would deadlock with us.
178+
*/
179+
up_write(&hmm->mirrors_sem);
180+
mirror->ops->release(mirror);
181+
down_write(&hmm->mirrors_sem);
182+
}
183+
mirror = list_first_entry_or_null(&hmm->mirrors,
184+
struct hmm_mirror, list);
185+
}
186+
up_write(&hmm->mirrors_sem);
187+
}
188+
163189
static void hmm_invalidate_range_start(struct mmu_notifier *mn,
164190
struct mm_struct *mm,
165191
unsigned long start,
@@ -185,6 +211,7 @@ static void hmm_invalidate_range_end(struct mmu_notifier *mn,
185211
}
186212

187213
static const struct mmu_notifier_ops hmm_mmu_notifier_ops = {
214+
.release = hmm_release,
188215
.invalidate_range_start = hmm_invalidate_range_start,
189216
.invalidate_range_end = hmm_invalidate_range_end,
190217
};
@@ -230,7 +257,7 @@ void hmm_mirror_unregister(struct hmm_mirror *mirror)
230257
struct hmm *hmm = mirror->hmm;
231258

232259
down_write(&hmm->mirrors_sem);
233-
list_del(&mirror->list);
260+
list_del_init(&mirror->list);
234261
up_write(&hmm->mirrors_sem);
235262
}
236263
EXPORT_SYMBOL(hmm_mirror_unregister);

0 commit comments

Comments
 (0)