Skip to content

Commit 040e123

Browse files
committed
drm/i915/gem: Avoid kmalloc under i915->mm_lock
Rearrange the allocation of the mm_struct registration to avoid allocating underneath the i915->mm_lock, so that we avoid tainting the lock (and in turn many other locks that may be held as i915->mm_lock is taken, and those locks we may want on the free [shrinker] paths). In doing so, we convert the lookup to be RCU protected by courtesy of converting the free-worker to be an rcu_work. v2: Remember to use hash_rcu variants to protect the list iteration from concurrent add/del. Signed-off-by: Chris Wilson <[email protected]> Reviewed-by: Matthew Auld <[email protected]> Link: https://patchwork.freedesktop.org/patch/msgid/[email protected]
1 parent f6a7d39 commit 040e123

File tree

2 files changed

+65
-68
lines changed

2 files changed

+65
-68
lines changed

drivers/gpu/drm/i915/gem/i915_gem_userptr.c

Lines changed: 64 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ struct i915_mm_struct {
2121
struct i915_mmu_notifier *mn;
2222
struct hlist_node node;
2323
struct kref kref;
24-
struct work_struct work;
24+
struct rcu_work work;
2525
};
2626

2727
#if defined(CONFIG_MMU_NOTIFIER)
@@ -189,40 +189,31 @@ i915_gem_userptr_release__mmu_notifier(struct drm_i915_gem_object *obj)
189189
static struct i915_mmu_notifier *
190190
i915_mmu_notifier_find(struct i915_mm_struct *mm)
191191
{
192-
struct i915_mmu_notifier *mn;
193-
int err = 0;
192+
struct i915_mmu_notifier *mn, *old;
193+
int err;
194194

195-
mn = mm->mn;
196-
if (mn)
195+
mn = READ_ONCE(mm->mn);
196+
if (likely(mn))
197197
return mn;
198198

199199
mn = i915_mmu_notifier_create(mm);
200200
if (IS_ERR(mn))
201-
err = PTR_ERR(mn);
202-
203-
mmap_write_lock(mm->mm);
204-
mutex_lock(&mm->i915->mm_lock);
205-
if (mm->mn == NULL && !err) {
206-
/* Protected by mmap_lock (write-lock) */
207-
err = __mmu_notifier_register(&mn->mn, mm->mm);
208-
if (!err) {
209-
/* Protected by mm_lock */
210-
mm->mn = fetch_and_zero(&mn);
211-
}
212-
} else if (mm->mn) {
213-
/*
214-
* Someone else raced and successfully installed the mmu
215-
* notifier, we can cancel our own errors.
216-
*/
217-
err = 0;
201+
return mn;
202+
203+
err = mmu_notifier_register(&mn->mn, mm->mm);
204+
if (err) {
205+
kfree(mn);
206+
return ERR_PTR(err);
218207
}
219-
mutex_unlock(&mm->i915->mm_lock);
220-
mmap_write_unlock(mm->mm);
221208

222-
if (mn && !IS_ERR(mn))
209+
old = cmpxchg(&mm->mn, NULL, mn);
210+
if (old) {
211+
mmu_notifier_unregister(&mn->mn, mm->mm);
223212
kfree(mn);
213+
mn = old;
214+
}
224215

225-
return err ? ERR_PTR(err) : mm->mn;
216+
return mn;
226217
}
227218

228219
static int
@@ -301,23 +292,28 @@ i915_mmu_notifier_free(struct i915_mmu_notifier *mn,
301292
#endif
302293

303294
static struct i915_mm_struct *
304-
__i915_mm_struct_find(struct drm_i915_private *dev_priv, struct mm_struct *real)
295+
__i915_mm_struct_find(struct drm_i915_private *i915, struct mm_struct *real)
305296
{
306-
struct i915_mm_struct *mm;
307-
308-
/* Protected by dev_priv->mm_lock */
309-
hash_for_each_possible(dev_priv->mm_structs, mm, node, (unsigned long)real)
310-
if (mm->mm == real)
311-
return mm;
297+
struct i915_mm_struct *it, *mm = NULL;
298+
299+
rcu_read_lock();
300+
hash_for_each_possible_rcu(i915->mm_structs,
301+
it, node,
302+
(unsigned long)real)
303+
if (it->mm == real && kref_get_unless_zero(&it->kref)) {
304+
mm = it;
305+
break;
306+
}
307+
rcu_read_unlock();
312308

313-
return NULL;
309+
return mm;
314310
}
315311

316312
static int
317313
i915_gem_userptr_init__mm_struct(struct drm_i915_gem_object *obj)
318314
{
319-
struct drm_i915_private *dev_priv = to_i915(obj->base.dev);
320-
struct i915_mm_struct *mm;
315+
struct drm_i915_private *i915 = to_i915(obj->base.dev);
316+
struct i915_mm_struct *mm, *new;
321317
int ret = 0;
322318

323319
/* During release of the GEM object we hold the struct_mutex. This
@@ -330,39 +326,42 @@ i915_gem_userptr_init__mm_struct(struct drm_i915_gem_object *obj)
330326
* struct_mutex, i.e. we need to schedule a worker to do the clean
331327
* up.
332328
*/
333-
mutex_lock(&dev_priv->mm_lock);
334-
mm = __i915_mm_struct_find(dev_priv, current->mm);
335-
if (mm == NULL) {
336-
mm = kmalloc(sizeof(*mm), GFP_KERNEL);
337-
if (mm == NULL) {
338-
ret = -ENOMEM;
339-
goto out;
340-
}
329+
mm = __i915_mm_struct_find(i915, current->mm);
330+
if (mm)
331+
goto out;
341332

342-
kref_init(&mm->kref);
343-
mm->i915 = to_i915(obj->base.dev);
333+
new = kmalloc(sizeof(*mm), GFP_KERNEL);
334+
if (!new)
335+
return -ENOMEM;
344336

345-
mm->mm = current->mm;
337+
kref_init(&new->kref);
338+
new->i915 = to_i915(obj->base.dev);
339+
new->mm = current->mm;
340+
new->mn = NULL;
341+
342+
spin_lock(&i915->mm_lock);
343+
mm = __i915_mm_struct_find(i915, current->mm);
344+
if (!mm) {
345+
hash_add_rcu(i915->mm_structs,
346+
&new->node,
347+
(unsigned long)new->mm);
346348
mmgrab(current->mm);
349+
mm = new;
350+
}
351+
spin_unlock(&i915->mm_lock);
352+
if (mm != new)
353+
kfree(new);
347354

348-
mm->mn = NULL;
349-
350-
/* Protected by dev_priv->mm_lock */
351-
hash_add(dev_priv->mm_structs,
352-
&mm->node, (unsigned long)mm->mm);
353-
} else
354-
kref_get(&mm->kref);
355-
356-
obj->userptr.mm = mm;
357355
out:
358-
mutex_unlock(&dev_priv->mm_lock);
356+
obj->userptr.mm = mm;
359357
return ret;
360358
}
361359

362360
static void
363361
__i915_mm_struct_free__worker(struct work_struct *work)
364362
{
365-
struct i915_mm_struct *mm = container_of(work, typeof(*mm), work);
363+
struct i915_mm_struct *mm = container_of(work, typeof(*mm), work.work);
364+
366365
i915_mmu_notifier_free(mm->mn, mm->mm);
367366
mmdrop(mm->mm);
368367
kfree(mm);
@@ -373,12 +372,12 @@ __i915_mm_struct_free(struct kref *kref)
373372
{
374373
struct i915_mm_struct *mm = container_of(kref, typeof(*mm), kref);
375374

376-
/* Protected by dev_priv->mm_lock */
377-
hash_del(&mm->node);
378-
mutex_unlock(&mm->i915->mm_lock);
375+
spin_lock(&mm->i915->mm_lock);
376+
hash_del_rcu(&mm->node);
377+
spin_unlock(&mm->i915->mm_lock);
379378

380-
INIT_WORK(&mm->work, __i915_mm_struct_free__worker);
381-
queue_work(mm->i915->mm.userptr_wq, &mm->work);
379+
INIT_RCU_WORK(&mm->work, __i915_mm_struct_free__worker);
380+
queue_rcu_work(system_wq, &mm->work);
382381
}
383382

384383
static void
@@ -387,9 +386,7 @@ i915_gem_userptr_release__mm_struct(struct drm_i915_gem_object *obj)
387386
if (obj->userptr.mm == NULL)
388387
return;
389388

390-
kref_put_mutex(&obj->userptr.mm->kref,
391-
__i915_mm_struct_free,
392-
&to_i915(obj->base.dev)->mm_lock);
389+
kref_put(&obj->userptr.mm->kref, __i915_mm_struct_free);
393390
obj->userptr.mm = NULL;
394391
}
395392

@@ -851,7 +848,7 @@ i915_gem_userptr_ioctl(struct drm_device *dev,
851848

852849
int i915_gem_init_userptr(struct drm_i915_private *dev_priv)
853850
{
854-
mutex_init(&dev_priv->mm_lock);
851+
spin_lock_init(&dev_priv->mm_lock);
855852
hash_init(dev_priv->mm_structs);
856853

857854
dev_priv->mm.userptr_wq =

drivers/gpu/drm/i915/i915_drv.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -993,7 +993,7 @@ struct drm_i915_private {
993993

994994
struct i915_gem_mm mm;
995995
DECLARE_HASHTABLE(mm_structs, 7);
996-
struct mutex mm_lock;
996+
spinlock_t mm_lock;
997997

998998
/* Kernel Modesetting */
999999

0 commit comments

Comments
 (0)