Skip to content

Commit 379e8f1

Browse files
committed
drm/gem: Make the GEM LRU lock part of drm_device
Recently, a few races have been discovered in the GEM LRU logic, all of them caused by the fact the LRU lock is accessed through gem->lru->lock, and that very same lock also protects changes to gem->lru, leading to situations where gem->lru needs to first be accessed without the lock held, to then get the lru to access the lock through and finally take the lock and do the expected operation. Currently, the only driver making use of this API (MSM) declares a device-wide lock, and the user we're about to add (panthor) will do the same. There's no evidence that we will ever have a driver that wants different pools of LRUs protected by different locks under the same drm_device. So we're better off moving this lock to drm_device and always locking it through obj->dev->gem_lru_mutex, or directly through dev->gem_lru_mutex. If anyone ever needs more fine-grained locking, this can be revisited to pass some drm_gem_lru_pool object representing the pool of LRUs under a specific lock, but for now, the per-device lock seems to be enough. Fixes: e7c2af1 ("drm/gem: Add LRU/shrinker helper") Reported-by: Chia-I Wu <olvaffe@gmail.com> Closes: https://gitlab.freedesktop.org/panfrost/linux/-/work_items/86 Reviewed-by: Rob Clark <rob.clark@oss.qualcomm.com> Reviewed-by: Liviu Dudau <liviu.dudau@arm.com> Reviewed-by: Steven Price <steven.price@arm.com> Reviewed-by: Chia-I Wu <olvaffe@gmail.com> Link: https://patch.msgid.link/20260518-panthor-shrinker-fixes-v4-1-1920234470d5@collabora.com Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
1 parent e02b526 commit 379e8f1

11 files changed

Lines changed: 69 additions & 75 deletions

File tree

drivers/gpu/drm/drm_drv.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -697,6 +697,7 @@ static void drm_dev_init_release(struct drm_device *dev, void *res)
697697
mutex_destroy(&dev->master_mutex);
698698
mutex_destroy(&dev->clientlist_mutex);
699699
mutex_destroy(&dev->filelist_mutex);
700+
mutex_destroy(&dev->gem_lru_mutex);
700701
}
701702

702703
static int drm_dev_init(struct drm_device *dev,
@@ -738,6 +739,7 @@ static int drm_dev_init(struct drm_device *dev,
738739
INIT_LIST_HEAD(&dev->vblank_event_list);
739740

740741
spin_lock_init(&dev->event_lock);
742+
mutex_init(&dev->gem_lru_mutex);
741743
mutex_init(&dev->filelist_mutex);
742744
mutex_init(&dev->clientlist_mutex);
743745
mutex_init(&dev->master_mutex);

drivers/gpu/drm/drm_gem.c

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1541,12 +1541,10 @@ EXPORT_SYMBOL(drm_gem_unlock_reservations);
15411541
* drm_gem_lru_init - initialize a LRU
15421542
*
15431543
* @lru: The LRU to initialize
1544-
* @lock: The lock protecting the LRU
15451544
*/
15461545
void
1547-
drm_gem_lru_init(struct drm_gem_lru *lru, struct mutex *lock)
1546+
drm_gem_lru_init(struct drm_gem_lru *lru)
15481547
{
1549-
lru->lock = lock;
15501548
lru->count = 0;
15511549
INIT_LIST_HEAD(&lru->list);
15521550
}
@@ -1571,14 +1569,10 @@ drm_gem_lru_remove_locked(struct drm_gem_object *obj)
15711569
void
15721570
drm_gem_lru_remove(struct drm_gem_object *obj)
15731571
{
1574-
struct drm_gem_lru *lru = obj->lru;
1575-
1576-
if (!lru)
1577-
return;
1578-
1579-
mutex_lock(lru->lock);
1580-
drm_gem_lru_remove_locked(obj);
1581-
mutex_unlock(lru->lock);
1572+
mutex_lock(&obj->dev->gem_lru_mutex);
1573+
if (obj->lru)
1574+
drm_gem_lru_remove_locked(obj);
1575+
mutex_unlock(&obj->dev->gem_lru_mutex);
15821576
}
15831577
EXPORT_SYMBOL(drm_gem_lru_remove);
15841578

@@ -1593,7 +1587,7 @@ EXPORT_SYMBOL(drm_gem_lru_remove);
15931587
void
15941588
drm_gem_lru_move_tail_locked(struct drm_gem_lru *lru, struct drm_gem_object *obj)
15951589
{
1596-
lockdep_assert_held_once(lru->lock);
1590+
lockdep_assert_held_once(&obj->dev->gem_lru_mutex);
15971591

15981592
if (obj->lru)
15991593
drm_gem_lru_remove_locked(obj);
@@ -1617,9 +1611,9 @@ EXPORT_SYMBOL(drm_gem_lru_move_tail_locked);
16171611
void
16181612
drm_gem_lru_move_tail(struct drm_gem_lru *lru, struct drm_gem_object *obj)
16191613
{
1620-
mutex_lock(lru->lock);
1614+
mutex_lock(&obj->dev->gem_lru_mutex);
16211615
drm_gem_lru_move_tail_locked(lru, obj);
1622-
mutex_unlock(lru->lock);
1616+
mutex_unlock(&obj->dev->gem_lru_mutex);
16231617
}
16241618
EXPORT_SYMBOL(drm_gem_lru_move_tail);
16251619

@@ -1633,14 +1627,16 @@ EXPORT_SYMBOL(drm_gem_lru_move_tail);
16331627
* of the shrink callback to check for this (ie. dma_resv_test_signaled())
16341628
* or if necessary block until the buffer becomes idle.
16351629
*
1630+
* @dev: DRM device the LRU belongs to
16361631
* @lru: The LRU to scan
16371632
* @nr_to_scan: The number of pages to try to reclaim
16381633
* @remaining: The number of pages left to reclaim, should be initialized by caller
16391634
* @shrink: Callback to try to shrink/reclaim the object.
16401635
* @ticket: Optional ww_acquire_ctx context to use for locking
16411636
*/
16421637
unsigned long
1643-
drm_gem_lru_scan(struct drm_gem_lru *lru,
1638+
drm_gem_lru_scan(struct drm_device *dev,
1639+
struct drm_gem_lru *lru,
16441640
unsigned int nr_to_scan,
16451641
unsigned long *remaining,
16461642
bool (*shrink)(struct drm_gem_object *obj, struct ww_acquire_ctx *ticket),
@@ -1650,9 +1646,9 @@ drm_gem_lru_scan(struct drm_gem_lru *lru,
16501646
struct drm_gem_object *obj;
16511647
unsigned freed = 0;
16521648

1653-
drm_gem_lru_init(&still_in_lru, lru->lock);
1649+
drm_gem_lru_init(&still_in_lru);
16541650

1655-
mutex_lock(lru->lock);
1651+
mutex_lock(&dev->gem_lru_mutex);
16561652

16571653
while (freed < nr_to_scan) {
16581654
obj = list_first_entry_or_null(&lru->list, typeof(*obj), lru_node);
@@ -1675,7 +1671,7 @@ drm_gem_lru_scan(struct drm_gem_lru *lru,
16751671
* rest of the loop body, to reduce contention with other
16761672
* code paths that need the LRU lock
16771673
*/
1678-
mutex_unlock(lru->lock);
1674+
mutex_unlock(&dev->gem_lru_mutex);
16791675

16801676
if (ticket)
16811677
ww_acquire_init(ticket, &reservation_ww_class);
@@ -1709,7 +1705,7 @@ drm_gem_lru_scan(struct drm_gem_lru *lru,
17091705

17101706
tail:
17111707
drm_gem_object_put(obj);
1712-
mutex_lock(lru->lock);
1708+
mutex_lock(&dev->gem_lru_mutex);
17131709
}
17141710

17151711
/*
@@ -1721,7 +1717,7 @@ drm_gem_lru_scan(struct drm_gem_lru *lru,
17211717
list_splice_tail(&still_in_lru.list, &lru->list);
17221718
lru->count += still_in_lru.count;
17231719

1724-
mutex_unlock(lru->lock);
1720+
mutex_unlock(&dev->gem_lru_mutex);
17251721

17261722
return freed;
17271723
}

drivers/gpu/drm/msm/msm_drv.c

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -128,19 +128,18 @@ static int msm_drm_init(struct device *dev, const struct drm_driver *drv,
128128
/*
129129
* Initialize the LRUs:
130130
*/
131-
mutex_init(&priv->lru.lock);
132-
drm_gem_lru_init(&priv->lru.unbacked, &priv->lru.lock);
133-
drm_gem_lru_init(&priv->lru.pinned, &priv->lru.lock);
134-
drm_gem_lru_init(&priv->lru.willneed, &priv->lru.lock);
135-
drm_gem_lru_init(&priv->lru.dontneed, &priv->lru.lock);
131+
drm_gem_lru_init(&priv->lru.unbacked);
132+
drm_gem_lru_init(&priv->lru.pinned);
133+
drm_gem_lru_init(&priv->lru.willneed);
134+
drm_gem_lru_init(&priv->lru.dontneed);
136135

137136
/* Initialize stall-on-fault */
138137
spin_lock_init(&priv->fault_stall_lock);
139138
priv->stall_enabled = true;
140139

141140
/* Teach lockdep about lock ordering wrt. shrinker: */
142141
fs_reclaim_acquire(GFP_KERNEL);
143-
might_lock(&priv->lru.lock);
142+
might_lock(&ddev->gem_lru_mutex);
144143
fs_reclaim_release(GFP_KERNEL);
145144

146145
if (priv->kms_init) {

drivers/gpu/drm/msm/msm_drv.h

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -150,13 +150,6 @@ struct msm_drm_private {
150150
* DONTNEED state (ie. can be purged)
151151
*/
152152
struct drm_gem_lru dontneed;
153-
154-
/**
155-
* lock:
156-
*
157-
* Protects manipulation of all of the LRUs.
158-
*/
159-
struct mutex lock;
160153
} lru;
161154

162155
struct notifier_block vmap_notifier;

drivers/gpu/drm/msm/msm_gem.c

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -177,11 +177,11 @@ static void update_lru_locked(struct drm_gem_object *obj)
177177

178178
static void update_lru(struct drm_gem_object *obj)
179179
{
180-
struct msm_drm_private *priv = obj->dev->dev_private;
180+
struct drm_device *dev = obj->dev;
181181

182-
mutex_lock(&priv->lru.lock);
182+
mutex_lock(&dev->gem_lru_mutex);
183183
update_lru_locked(obj);
184-
mutex_unlock(&priv->lru.lock);
184+
mutex_unlock(&dev->gem_lru_mutex);
185185
}
186186

187187
static struct page **get_pages(struct drm_gem_object *obj)
@@ -292,11 +292,11 @@ void msm_gem_pin_obj_locked(struct drm_gem_object *obj)
292292

293293
static void pin_obj_locked(struct drm_gem_object *obj)
294294
{
295-
struct msm_drm_private *priv = obj->dev->dev_private;
295+
struct drm_device *dev = obj->dev;
296296

297-
mutex_lock(&priv->lru.lock);
297+
mutex_lock(&dev->gem_lru_mutex);
298298
msm_gem_pin_obj_locked(obj);
299-
mutex_unlock(&priv->lru.lock);
299+
mutex_unlock(&dev->gem_lru_mutex);
300300
}
301301

302302
struct page **msm_gem_pin_pages_locked(struct drm_gem_object *obj)
@@ -487,16 +487,16 @@ int msm_gem_pin_vma_locked(struct drm_gem_object *obj, struct drm_gpuva *vma)
487487

488488
void msm_gem_unpin_locked(struct drm_gem_object *obj)
489489
{
490-
struct msm_drm_private *priv = obj->dev->dev_private;
490+
struct drm_device *dev = obj->dev;
491491
struct msm_gem_object *msm_obj = to_msm_bo(obj);
492492

493493
msm_gem_assert_locked(obj);
494494

495-
mutex_lock(&priv->lru.lock);
495+
mutex_lock(&dev->gem_lru_mutex);
496496
msm_obj->pin_count--;
497497
GEM_WARN_ON(msm_obj->pin_count < 0);
498498
update_lru_locked(obj);
499-
mutex_unlock(&priv->lru.lock);
499+
mutex_unlock(&dev->gem_lru_mutex);
500500
}
501501

502502
/* Special unpin path for use in fence-signaling path, avoiding the need
@@ -507,10 +507,10 @@ void msm_gem_unpin_locked(struct drm_gem_object *obj)
507507
*/
508508
void msm_gem_unpin_active(struct drm_gem_object *obj)
509509
{
510-
struct msm_drm_private *priv = obj->dev->dev_private;
510+
struct drm_device *dev = obj->dev;
511511
struct msm_gem_object *msm_obj = to_msm_bo(obj);
512512

513-
GEM_WARN_ON(!mutex_is_locked(&priv->lru.lock));
513+
GEM_WARN_ON(!mutex_is_locked(&dev->gem_lru_mutex));
514514

515515
msm_obj->pin_count--;
516516
GEM_WARN_ON(msm_obj->pin_count < 0);
@@ -797,12 +797,12 @@ void msm_gem_put_vaddr(struct drm_gem_object *obj)
797797
*/
798798
int msm_gem_madvise(struct drm_gem_object *obj, unsigned madv)
799799
{
800-
struct msm_drm_private *priv = obj->dev->dev_private;
800+
struct drm_device *dev = obj->dev;
801801
struct msm_gem_object *msm_obj = to_msm_bo(obj);
802802

803803
msm_gem_lock(obj);
804804

805-
mutex_lock(&priv->lru.lock);
805+
mutex_lock(&dev->gem_lru_mutex);
806806

807807
if (msm_obj->madv != __MSM_MADV_PURGED)
808808
msm_obj->madv = madv;
@@ -814,7 +814,7 @@ int msm_gem_madvise(struct drm_gem_object *obj, unsigned madv)
814814
*/
815815
update_lru_locked(obj);
816816

817-
mutex_unlock(&priv->lru.lock);
817+
mutex_unlock(&dev->gem_lru_mutex);
818818

819819
msm_gem_unlock(obj);
820820

@@ -824,7 +824,6 @@ int msm_gem_madvise(struct drm_gem_object *obj, unsigned madv)
824824
void msm_gem_purge(struct drm_gem_object *obj)
825825
{
826826
struct drm_device *dev = obj->dev;
827-
struct msm_drm_private *priv = obj->dev->dev_private;
828827
struct msm_gem_object *msm_obj = to_msm_bo(obj);
829828

830829
msm_gem_assert_locked(obj);
@@ -839,10 +838,10 @@ void msm_gem_purge(struct drm_gem_object *obj)
839838

840839
put_pages(obj);
841840

842-
mutex_lock(&priv->lru.lock);
841+
mutex_lock(&dev->gem_lru_mutex);
843842
/* A one-way transition: */
844843
msm_obj->madv = __MSM_MADV_PURGED;
845-
mutex_unlock(&priv->lru.lock);
844+
mutex_unlock(&dev->gem_lru_mutex);
846845

847846
drm_gem_free_mmap_offset(obj);
848847

drivers/gpu/drm/msm/msm_gem_shrinker.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ msm_gem_shrinker_scan(struct shrinker *shrinker, struct shrink_control *sc)
186186
if (!stages[i].cond)
187187
continue;
188188
stages[i].freed =
189-
drm_gem_lru_scan(stages[i].lru, nr,
189+
drm_gem_lru_scan(priv->dev, stages[i].lru, nr,
190190
&stages[i].remaining,
191191
stages[i].shrink,
192192
&ticket);
@@ -255,7 +255,7 @@ msm_gem_shrinker_vmap(struct notifier_block *nb, unsigned long event, void *ptr)
255255
unsigned long remaining = 0;
256256

257257
for (idx = 0; lrus[idx] && unmapped < vmap_shrink_limit; idx++) {
258-
unmapped += drm_gem_lru_scan(lrus[idx],
258+
unmapped += drm_gem_lru_scan(priv->dev, lrus[idx],
259259
vmap_shrink_limit - unmapped,
260260
&remaining,
261261
vmap_shrink,

drivers/gpu/drm/msm/msm_gem_submit.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,7 @@ static int submit_fence_sync(struct msm_gem_submit *submit)
352352

353353
static int submit_pin_objects(struct msm_gem_submit *submit)
354354
{
355-
struct msm_drm_private *priv = submit->dev->dev_private;
355+
struct drm_device *dev = submit->dev;
356356
int i, ret = 0;
357357

358358
for (i = 0; i < submit->nr_bos; i++) {
@@ -381,11 +381,11 @@ static int submit_pin_objects(struct msm_gem_submit *submit)
381381
* get_pages() which could trigger reclaim.. and if we held the LRU lock
382382
* could trigger deadlock with the shrinker).
383383
*/
384-
mutex_lock(&priv->lru.lock);
384+
mutex_lock(&dev->gem_lru_mutex);
385385
for (i = 0; i < submit->nr_bos; i++) {
386386
msm_gem_pin_obj_locked(submit->bos[i].obj);
387387
}
388-
mutex_unlock(&priv->lru.lock);
388+
mutex_unlock(&dev->gem_lru_mutex);
389389

390390
submit->bos_pinned = true;
391391

drivers/gpu/drm/msm/msm_gem_vma.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -702,7 +702,7 @@ static struct dma_fence *
702702
msm_vma_job_run(struct drm_sched_job *_job)
703703
{
704704
struct msm_vm_bind_job *job = to_msm_vm_bind_job(_job);
705-
struct msm_drm_private *priv = job->vm->drm->dev_private;
705+
struct drm_device *dev = job->vm->drm;
706706
struct msm_gem_vm *vm = to_msm_vm(job->vm);
707707
struct drm_gem_object *obj;
708708
int ret = vm->unusable ? -EINVAL : 0;
@@ -745,13 +745,13 @@ msm_vma_job_run(struct drm_sched_job *_job)
745745
if (ret)
746746
msm_gem_vm_unusable(job->vm);
747747

748-
mutex_lock(&priv->lru.lock);
748+
mutex_lock(&dev->gem_lru_mutex);
749749

750750
job_foreach_bo (obj, job) {
751751
msm_gem_unpin_active(obj);
752752
}
753753

754-
mutex_unlock(&priv->lru.lock);
754+
mutex_unlock(&dev->gem_lru_mutex);
755755

756756
/* VM_BIND ops are synchronous, so no fence to wait on: */
757757
return NULL;
@@ -1305,7 +1305,7 @@ vm_bind_job_pin_objects(struct msm_vm_bind_job *job)
13051305
return PTR_ERR(pages);
13061306
}
13071307

1308-
struct msm_drm_private *priv = job->vm->drm->dev_private;
1308+
struct drm_device *dev = job->vm->drm;
13091309

13101310
/*
13111311
* A second loop while holding the LRU lock (a) avoids acquiring/dropping
@@ -1314,10 +1314,10 @@ vm_bind_job_pin_objects(struct msm_vm_bind_job *job)
13141314
* get_pages() which could trigger reclaim.. and if we held the LRU lock
13151315
* could trigger deadlock with the shrinker).
13161316
*/
1317-
mutex_lock(&priv->lru.lock);
1317+
mutex_lock(&dev->gem_lru_mutex);
13181318
job_foreach_bo (obj, job)
13191319
msm_gem_pin_obj_locked(obj);
1320-
mutex_unlock(&priv->lru.lock);
1320+
mutex_unlock(&dev->gem_lru_mutex);
13211321

13221322
job->bos_pinned = true;
13231323

drivers/gpu/drm/msm/msm_ringbuffer.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,13 @@ static struct dma_fence *msm_job_run(struct drm_sched_job *job)
1616
struct msm_gem_submit *submit = to_msm_submit(job);
1717
struct msm_fence_context *fctx = submit->ring->fctx;
1818
struct msm_gpu *gpu = submit->gpu;
19-
struct msm_drm_private *priv = gpu->dev->dev_private;
19+
struct drm_device *dev = gpu->dev;
2020
unsigned nr_cmds = submit->nr_cmds;
2121
int i;
2222

2323
msm_fence_init(submit->hw_fence, fctx);
2424

25-
mutex_lock(&priv->lru.lock);
25+
mutex_lock(&dev->gem_lru_mutex);
2626

2727
for (i = 0; i < submit->nr_bos; i++) {
2828
struct drm_gem_object *obj = submit->bos[i].obj;
@@ -32,7 +32,7 @@ static struct dma_fence *msm_job_run(struct drm_sched_job *job)
3232

3333
submit->bos_pinned = false;
3434

35-
mutex_unlock(&priv->lru.lock);
35+
mutex_unlock(&dev->gem_lru_mutex);
3636

3737
/* TODO move submit path over to using a per-ring lock.. */
3838
mutex_lock(&gpu->lock);

0 commit comments

Comments
 (0)