@@ -233,13 +233,24 @@ int hmm_mirror_register(struct hmm_mirror *mirror, struct mm_struct *mm)
233
233
if (!mm || !mirror || !mirror -> ops )
234
234
return - EINVAL ;
235
235
236
+ again :
236
237
mirror -> hmm = hmm_register (mm );
237
238
if (!mirror -> hmm )
238
239
return - ENOMEM ;
239
240
240
241
down_write (& mirror -> hmm -> mirrors_sem );
241
- list_add (& mirror -> list , & mirror -> hmm -> mirrors );
242
- up_write (& mirror -> hmm -> mirrors_sem );
242
+ if (mirror -> hmm -> mm == NULL ) {
243
+ /*
244
+ * A racing hmm_mirror_unregister() is about to destroy the hmm
245
+ * struct. Try again to allocate a new one.
246
+ */
247
+ up_write (& mirror -> hmm -> mirrors_sem );
248
+ mirror -> hmm = NULL ;
249
+ goto again ;
250
+ } else {
251
+ list_add (& mirror -> list , & mirror -> hmm -> mirrors );
252
+ up_write (& mirror -> hmm -> mirrors_sem );
253
+ }
243
254
244
255
return 0 ;
245
256
}
@@ -254,11 +265,32 @@ EXPORT_SYMBOL(hmm_mirror_register);
254
265
*/
255
266
void hmm_mirror_unregister (struct hmm_mirror * mirror )
256
267
{
257
- struct hmm * hmm = mirror -> hmm ;
268
+ bool should_unregister = false;
269
+ struct mm_struct * mm ;
270
+ struct hmm * hmm ;
271
+
272
+ if (mirror -> hmm == NULL )
273
+ return ;
258
274
275
+ hmm = mirror -> hmm ;
259
276
down_write (& hmm -> mirrors_sem );
260
277
list_del_init (& mirror -> list );
278
+ should_unregister = list_empty (& hmm -> mirrors );
279
+ mirror -> hmm = NULL ;
280
+ mm = hmm -> mm ;
281
+ hmm -> mm = NULL ;
261
282
up_write (& hmm -> mirrors_sem );
283
+
284
+ if (!should_unregister || mm == NULL )
285
+ return ;
286
+
287
+ spin_lock (& mm -> page_table_lock );
288
+ if (mm -> hmm == hmm )
289
+ mm -> hmm = NULL ;
290
+ spin_unlock (& mm -> page_table_lock );
291
+
292
+ mmu_notifier_unregister_no_release (& hmm -> mmu_notifier , mm );
293
+ kfree (hmm );
262
294
}
263
295
EXPORT_SYMBOL (hmm_mirror_unregister );
264
296
0 commit comments