1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24#include <drm/drmP.h>
25#include <drm/drm_crtc.h>
26#include <drm/drm_modeset_lock.h>
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70int __drm_modeset_lock_all(struct drm_device *dev,
71 bool trylock)
72{
73 struct drm_mode_config *config = &dev->mode_config;
74 struct drm_modeset_acquire_ctx *ctx;
75 int ret;
76
77 ctx = kzalloc(sizeof(*ctx),
78 trylock ? GFP_ATOMIC : GFP_KERNEL);
79 if (!ctx)
80 return -ENOMEM;
81
82 if (trylock) {
83 if (!mutex_trylock(&config->mutex))
84 return -EBUSY;
85 } else {
86 mutex_lock(&config->mutex);
87 }
88
89 drm_modeset_acquire_init(ctx, 0);
90 ctx->trylock_only = trylock;
91
92retry:
93 ret = drm_modeset_lock(&config->connection_mutex, ctx);
94 if (ret)
95 goto fail;
96 ret = drm_modeset_lock_all_crtcs(dev, ctx);
97 if (ret)
98 goto fail;
99
100 WARN_ON(config->acquire_ctx);
101
102
103
104
105 config->acquire_ctx = ctx;
106
107 drm_warn_on_modeset_not_all_locked(dev);
108
109 return 0;
110
111fail:
112 if (ret == -EDEADLK) {
113 drm_modeset_backoff(ctx);
114 goto retry;
115 }
116
117 return ret;
118}
119EXPORT_SYMBOL(__drm_modeset_lock_all);
120
121
122
123
124
125
126
127
128
129void drm_modeset_lock_all(struct drm_device *dev)
130{
131 WARN_ON(__drm_modeset_lock_all(dev, false) != 0);
132}
133EXPORT_SYMBOL(drm_modeset_lock_all);
134
135
136
137
138
139
140
141void drm_modeset_unlock_all(struct drm_device *dev)
142{
143 struct drm_mode_config *config = &dev->mode_config;
144 struct drm_modeset_acquire_ctx *ctx = config->acquire_ctx;
145
146 if (WARN_ON(!ctx))
147 return;
148
149 config->acquire_ctx = NULL;
150 drm_modeset_drop_locks(ctx);
151 drm_modeset_acquire_fini(ctx);
152
153 kfree(ctx);
154
155 mutex_unlock(&dev->mode_config.mutex);
156}
157EXPORT_SYMBOL(drm_modeset_unlock_all);
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172void drm_modeset_lock_crtc(struct drm_crtc *crtc,
173 struct drm_plane *plane)
174{
175 struct drm_modeset_acquire_ctx *ctx;
176 int ret;
177
178 ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
179 if (WARN_ON(!ctx))
180 return;
181
182 drm_modeset_acquire_init(ctx, 0);
183
184retry:
185 ret = drm_modeset_lock(&crtc->mutex, ctx);
186 if (ret)
187 goto fail;
188
189 if (plane) {
190 ret = drm_modeset_lock(&plane->mutex, ctx);
191 if (ret)
192 goto fail;
193
194 if (plane->crtc) {
195 ret = drm_modeset_lock(&plane->crtc->mutex, ctx);
196 if (ret)
197 goto fail;
198 }
199 }
200
201 WARN_ON(crtc->acquire_ctx);
202
203
204
205
206 crtc->acquire_ctx = ctx;
207
208 return;
209
210fail:
211 if (ret == -EDEADLK) {
212 drm_modeset_backoff(ctx);
213 goto retry;
214 }
215}
216EXPORT_SYMBOL(drm_modeset_lock_crtc);
217
218
219
220
221
222
223
224
225
226
227struct drm_modeset_acquire_ctx *
228drm_modeset_legacy_acquire_ctx(struct drm_crtc *crtc)
229{
230 if (crtc->acquire_ctx)
231 return crtc->acquire_ctx;
232
233 WARN_ON(!crtc->dev->mode_config.acquire_ctx);
234
235 return crtc->dev->mode_config.acquire_ctx;
236}
237EXPORT_SYMBOL(drm_modeset_legacy_acquire_ctx);
238
239
240
241
242
243
244
245
246void drm_modeset_unlock_crtc(struct drm_crtc *crtc)
247{
248 struct drm_modeset_acquire_ctx *ctx = crtc->acquire_ctx;
249
250 if (WARN_ON(!ctx))
251 return;
252
253 crtc->acquire_ctx = NULL;
254 drm_modeset_drop_locks(ctx);
255 drm_modeset_acquire_fini(ctx);
256
257 kfree(ctx);
258}
259EXPORT_SYMBOL(drm_modeset_unlock_crtc);
260
261
262
263
264
265
266
267void drm_warn_on_modeset_not_all_locked(struct drm_device *dev)
268{
269 struct drm_crtc *crtc;
270
271
272 if (oops_in_progress)
273 return;
274
275 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
276 WARN_ON(!drm_modeset_is_locked(&crtc->mutex));
277
278 WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
279 WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
280}
281EXPORT_SYMBOL(drm_warn_on_modeset_not_all_locked);
282
283
284
285
286
287
288void drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx,
289 uint32_t flags)
290{
291 memset(ctx, 0, sizeof(*ctx));
292 ww_acquire_init(&ctx->ww_ctx, &crtc_ww_class);
293 INIT_LIST_HEAD(&ctx->locked);
294}
295EXPORT_SYMBOL(drm_modeset_acquire_init);
296
297
298
299
300
301void drm_modeset_acquire_fini(struct drm_modeset_acquire_ctx *ctx)
302{
303 ww_acquire_fini(&ctx->ww_ctx);
304}
305EXPORT_SYMBOL(drm_modeset_acquire_fini);
306
307
308
309
310
311
312
313void drm_modeset_drop_locks(struct drm_modeset_acquire_ctx *ctx)
314{
315 WARN_ON(ctx->contended);
316 while (!list_empty(&ctx->locked)) {
317 struct drm_modeset_lock *lock;
318
319 lock = list_first_entry(&ctx->locked,
320 struct drm_modeset_lock, head);
321
322 drm_modeset_unlock(lock);
323 }
324}
325EXPORT_SYMBOL(drm_modeset_drop_locks);
326
327static inline int modeset_lock(struct drm_modeset_lock *lock,
328 struct drm_modeset_acquire_ctx *ctx,
329 bool interruptible, bool slow)
330{
331 int ret;
332
333 WARN_ON(ctx->contended);
334
335 if (ctx->trylock_only) {
336 if (!ww_mutex_trylock(&lock->mutex))
337 return -EBUSY;
338 else
339 return 0;
340 } else if (interruptible && slow) {
341 ret = ww_mutex_lock_slow_interruptible(&lock->mutex, &ctx->ww_ctx);
342 } else if (interruptible) {
343 ret = ww_mutex_lock_interruptible(&lock->mutex, &ctx->ww_ctx);
344 } else if (slow) {
345 ww_mutex_lock_slow(&lock->mutex, &ctx->ww_ctx);
346 ret = 0;
347 } else {
348 ret = ww_mutex_lock(&lock->mutex, &ctx->ww_ctx);
349 }
350 if (!ret) {
351 WARN_ON(!list_empty(&lock->head));
352 list_add(&lock->head, &ctx->locked);
353 } else if (ret == -EALREADY) {
354
355
356
357
358
359 ret = 0;
360 } else if (ret == -EDEADLK) {
361 ctx->contended = lock;
362 }
363
364 return ret;
365}
366
367static int modeset_backoff(struct drm_modeset_acquire_ctx *ctx,
368 bool interruptible)
369{
370 struct drm_modeset_lock *contended = ctx->contended;
371
372 ctx->contended = NULL;
373
374 if (WARN_ON(!contended))
375 return 0;
376
377 drm_modeset_drop_locks(ctx);
378
379 return modeset_lock(contended, ctx, interruptible, true);
380}
381
382
383
384
385
386
387
388
389
390void drm_modeset_backoff(struct drm_modeset_acquire_ctx *ctx)
391{
392 modeset_backoff(ctx, false);
393}
394EXPORT_SYMBOL(drm_modeset_backoff);
395
396
397
398
399
400
401
402int drm_modeset_backoff_interruptible(struct drm_modeset_acquire_ctx *ctx)
403{
404 return modeset_backoff(ctx, true);
405}
406EXPORT_SYMBOL(drm_modeset_backoff_interruptible);
407
408
409
410
411
412
413
414
415
416
417
418
419int drm_modeset_lock(struct drm_modeset_lock *lock,
420 struct drm_modeset_acquire_ctx *ctx)
421{
422 if (ctx)
423 return modeset_lock(lock, ctx, false, false);
424
425 ww_mutex_lock(&lock->mutex, NULL);
426 return 0;
427}
428EXPORT_SYMBOL(drm_modeset_lock);
429
430
431
432
433
434
435
436
437int drm_modeset_lock_interruptible(struct drm_modeset_lock *lock,
438 struct drm_modeset_acquire_ctx *ctx)
439{
440 if (ctx)
441 return modeset_lock(lock, ctx, true, false);
442
443 return ww_mutex_lock_interruptible(&lock->mutex, NULL);
444}
445EXPORT_SYMBOL(drm_modeset_lock_interruptible);
446
447
448
449
450
451void drm_modeset_unlock(struct drm_modeset_lock *lock)
452{
453 list_del_init(&lock->head);
454 ww_mutex_unlock(&lock->mutex);
455}
456EXPORT_SYMBOL(drm_modeset_unlock);
457
458
459
460int drm_modeset_lock_all_crtcs(struct drm_device *dev,
461 struct drm_modeset_acquire_ctx *ctx)
462{
463 struct drm_mode_config *config = &dev->mode_config;
464 struct drm_crtc *crtc;
465 struct drm_plane *plane;
466 int ret = 0;
467
468 list_for_each_entry(crtc, &config->crtc_list, head) {
469 ret = drm_modeset_lock(&crtc->mutex, ctx);
470 if (ret)
471 return ret;
472 }
473
474 list_for_each_entry(plane, &config->plane_list, head) {
475 ret = drm_modeset_lock(&plane->mutex, ctx);
476 if (ret)
477 return ret;
478 }
479
480 return 0;
481}
482EXPORT_SYMBOL(drm_modeset_lock_all_crtcs);
483