1 | /* |
2 | * Copyright (C) 2014 Red Hat |
3 | * Author: Rob Clark <robdclark@gmail.com> |
4 | * |
5 | * Permission is hereby granted, free of charge, to any person obtaining a |
6 | * copy of this software and associated documentation files (the "Software"), |
7 | * to deal in the Software without restriction, including without limitation |
8 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
9 | * and/or sell copies of the Software, and to permit persons to whom the |
10 | * Software is furnished to do so, subject to the following conditions: |
11 | * |
12 | * The above copyright notice and this permission notice shall be included in |
13 | * all copies or substantial portions of the Software. |
14 | * |
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
18 | * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR |
19 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, |
20 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
21 | * OTHER DEALINGS IN THE SOFTWARE. |
22 | */ |
23 | |
24 | #include <drm/drm_atomic.h> |
25 | #include <drm/drm_crtc.h> |
26 | #include <drm/drm_device.h> |
27 | #include <drm/drm_modeset_lock.h> |
28 | #include <drm/drm_print.h> |
29 | |
30 | /** |
31 | * DOC: kms locking |
32 | * |
33 | * As KMS moves toward more fine grained locking, and atomic ioctl where |
34 | * userspace can indirectly control locking order, it becomes necessary |
35 | * to use &ww_mutex and acquire-contexts to avoid deadlocks. But because |
36 | * the locking is more distributed around the driver code, we want a bit |
37 | * of extra utility/tracking out of our acquire-ctx. This is provided |
38 | * by &struct drm_modeset_lock and &struct drm_modeset_acquire_ctx. |
39 | * |
40 | * For basic principles of &ww_mutex, see: Documentation/locking/ww-mutex-design.rst |
41 | * |
42 | * The basic usage pattern is to:: |
43 | * |
44 | * drm_modeset_acquire_init(ctx, DRM_MODESET_ACQUIRE_INTERRUPTIBLE) |
45 | * retry: |
46 | * foreach (lock in random_ordered_set_of_locks) { |
47 | * ret = drm_modeset_lock(lock, ctx) |
48 | * if (ret == -EDEADLK) { |
49 | * ret = drm_modeset_backoff(ctx); |
50 | * if (!ret) |
51 | * goto retry; |
52 | * } |
53 | * if (ret) |
54 | * goto out; |
55 | * } |
56 | * ... do stuff ... |
57 | * out: |
58 | * drm_modeset_drop_locks(ctx); |
59 | * drm_modeset_acquire_fini(ctx); |
60 | * |
61 | * For convenience this control flow is implemented in |
62 | * DRM_MODESET_LOCK_ALL_BEGIN() and DRM_MODESET_LOCK_ALL_END() for the case |
63 | * where all modeset locks need to be taken through drm_modeset_lock_all_ctx(). |
64 | * |
65 | * If all that is needed is a single modeset lock, then the &struct |
66 | * drm_modeset_acquire_ctx is not needed and the locking can be simplified |
67 | * by passing a NULL instead of ctx in the drm_modeset_lock() call or |
68 | * calling drm_modeset_lock_single_interruptible(). To unlock afterwards |
69 | * call drm_modeset_unlock(). |
70 | * |
71 | * On top of these per-object locks using &ww_mutex there's also an overall |
72 | * &drm_mode_config.mutex, for protecting everything else. Mostly this means |
73 | * probe state of connectors, and preventing hotplug add/removal of connectors. |
74 | * |
75 | * Finally there's a bunch of dedicated locks to protect drm core internal |
76 | * lists and lookup data structures. |
77 | */ |
78 | |
79 | static DEFINE_WW_CLASS(crtc_ww_class); |
80 | |
81 | #if IS_ENABLED(CONFIG_DRM_DEBUG_MODESET_LOCK) |
82 | static noinline depot_stack_handle_t __drm_stack_depot_save(void) |
83 | { |
84 | unsigned long entries[8]; |
85 | unsigned int n; |
86 | |
87 | n = stack_trace_save(store: entries, ARRAY_SIZE(entries), skipnr: 1); |
88 | |
89 | return stack_depot_save(entries, nr_entries: n, GFP_NOWAIT | __GFP_NOWARN); |
90 | } |
91 | |
92 | static void __drm_stack_depot_print(depot_stack_handle_t stack_depot) |
93 | { |
94 | struct drm_printer p = drm_debug_printer(prefix: "drm_modeset_lock" ); |
95 | unsigned long *entries; |
96 | unsigned int nr_entries; |
97 | char *buf; |
98 | |
99 | buf = kmalloc(PAGE_SIZE, GFP_NOWAIT | __GFP_NOWARN); |
100 | if (!buf) |
101 | return; |
102 | |
103 | nr_entries = stack_depot_fetch(handle: stack_depot, entries: &entries); |
104 | stack_trace_snprint(buf, PAGE_SIZE, entries, nr_entries, spaces: 2); |
105 | |
106 | drm_printf(p: &p, f: "attempting to lock a contended lock without backoff:\n%s" , buf); |
107 | |
108 | kfree(objp: buf); |
109 | } |
110 | |
111 | static void __drm_stack_depot_init(void) |
112 | { |
113 | stack_depot_init(); |
114 | } |
115 | #else /* CONFIG_DRM_DEBUG_MODESET_LOCK */ |
116 | static depot_stack_handle_t __drm_stack_depot_save(void) |
117 | { |
118 | return 0; |
119 | } |
120 | static void __drm_stack_depot_print(depot_stack_handle_t stack_depot) |
121 | { |
122 | } |
123 | static void __drm_stack_depot_init(void) |
124 | { |
125 | } |
126 | #endif /* CONFIG_DRM_DEBUG_MODESET_LOCK */ |
127 | |
128 | /** |
129 | * drm_modeset_lock_all - take all modeset locks |
130 | * @dev: DRM device |
131 | * |
132 | * This function takes all modeset locks, suitable where a more fine-grained |
133 | * scheme isn't (yet) implemented. Locks must be dropped by calling the |
134 | * drm_modeset_unlock_all() function. |
135 | * |
136 | * This function is deprecated. It allocates a lock acquisition context and |
137 | * stores it in &drm_device.mode_config. This facilitate conversion of |
138 | * existing code because it removes the need to manually deal with the |
139 | * acquisition context, but it is also brittle because the context is global |
140 | * and care must be taken not to nest calls. New code should use the |
141 | * drm_modeset_lock_all_ctx() function and pass in the context explicitly. |
142 | */ |
143 | void drm_modeset_lock_all(struct drm_device *dev) |
144 | { |
145 | struct drm_mode_config *config = &dev->mode_config; |
146 | struct drm_modeset_acquire_ctx *ctx; |
147 | int ret; |
148 | |
149 | ctx = kzalloc(size: sizeof(*ctx), GFP_KERNEL | __GFP_NOFAIL); |
150 | if (WARN_ON(!ctx)) |
151 | return; |
152 | |
153 | mutex_lock(&config->mutex); |
154 | |
155 | drm_modeset_acquire_init(ctx, flags: 0); |
156 | |
157 | retry: |
158 | ret = drm_modeset_lock_all_ctx(dev, ctx); |
159 | if (ret < 0) { |
160 | if (ret == -EDEADLK) { |
161 | drm_modeset_backoff(ctx); |
162 | goto retry; |
163 | } |
164 | |
165 | drm_modeset_acquire_fini(ctx); |
166 | kfree(objp: ctx); |
167 | return; |
168 | } |
169 | ww_acquire_done(ctx: &ctx->ww_ctx); |
170 | |
171 | WARN_ON(config->acquire_ctx); |
172 | |
173 | /* |
174 | * We hold the locks now, so it is safe to stash the acquisition |
175 | * context for drm_modeset_unlock_all(). |
176 | */ |
177 | config->acquire_ctx = ctx; |
178 | |
179 | drm_warn_on_modeset_not_all_locked(dev); |
180 | } |
181 | EXPORT_SYMBOL(drm_modeset_lock_all); |
182 | |
183 | /** |
184 | * drm_modeset_unlock_all - drop all modeset locks |
185 | * @dev: DRM device |
186 | * |
187 | * This function drops all modeset locks taken by a previous call to the |
188 | * drm_modeset_lock_all() function. |
189 | * |
190 | * This function is deprecated. It uses the lock acquisition context stored |
191 | * in &drm_device.mode_config. This facilitates conversion of existing |
192 | * code because it removes the need to manually deal with the acquisition |
193 | * context, but it is also brittle because the context is global and care must |
194 | * be taken not to nest calls. New code should pass the acquisition context |
195 | * directly to the drm_modeset_drop_locks() function. |
196 | */ |
197 | void drm_modeset_unlock_all(struct drm_device *dev) |
198 | { |
199 | struct drm_mode_config *config = &dev->mode_config; |
200 | struct drm_modeset_acquire_ctx *ctx = config->acquire_ctx; |
201 | |
202 | if (WARN_ON(!ctx)) |
203 | return; |
204 | |
205 | config->acquire_ctx = NULL; |
206 | drm_modeset_drop_locks(ctx); |
207 | drm_modeset_acquire_fini(ctx); |
208 | |
209 | kfree(objp: ctx); |
210 | |
211 | mutex_unlock(lock: &dev->mode_config.mutex); |
212 | } |
213 | EXPORT_SYMBOL(drm_modeset_unlock_all); |
214 | |
215 | /** |
216 | * drm_warn_on_modeset_not_all_locked - check that all modeset locks are locked |
217 | * @dev: device |
218 | * |
219 | * Useful as a debug assert. |
220 | */ |
221 | void drm_warn_on_modeset_not_all_locked(struct drm_device *dev) |
222 | { |
223 | struct drm_crtc *crtc; |
224 | |
225 | /* Locking is currently fubar in the panic handler. */ |
226 | if (oops_in_progress) |
227 | return; |
228 | |
229 | drm_for_each_crtc(crtc, dev) |
230 | WARN_ON(!drm_modeset_is_locked(&crtc->mutex)); |
231 | |
232 | WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex)); |
233 | WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); |
234 | } |
235 | EXPORT_SYMBOL(drm_warn_on_modeset_not_all_locked); |
236 | |
237 | /** |
238 | * drm_modeset_acquire_init - initialize acquire context |
239 | * @ctx: the acquire context |
240 | * @flags: 0 or %DRM_MODESET_ACQUIRE_INTERRUPTIBLE |
241 | * |
242 | * When passing %DRM_MODESET_ACQUIRE_INTERRUPTIBLE to @flags, |
243 | * all calls to drm_modeset_lock() will perform an interruptible |
244 | * wait. |
245 | */ |
246 | void drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx, |
247 | uint32_t flags) |
248 | { |
249 | memset(ctx, 0, sizeof(*ctx)); |
250 | ww_acquire_init(ctx: &ctx->ww_ctx, ww_class: &crtc_ww_class); |
251 | INIT_LIST_HEAD(list: &ctx->locked); |
252 | |
253 | if (flags & DRM_MODESET_ACQUIRE_INTERRUPTIBLE) |
254 | ctx->interruptible = true; |
255 | } |
256 | EXPORT_SYMBOL(drm_modeset_acquire_init); |
257 | |
258 | /** |
259 | * drm_modeset_acquire_fini - cleanup acquire context |
260 | * @ctx: the acquire context |
261 | */ |
262 | void drm_modeset_acquire_fini(struct drm_modeset_acquire_ctx *ctx) |
263 | { |
264 | ww_acquire_fini(ctx: &ctx->ww_ctx); |
265 | } |
266 | EXPORT_SYMBOL(drm_modeset_acquire_fini); |
267 | |
268 | /** |
269 | * drm_modeset_drop_locks - drop all locks |
270 | * @ctx: the acquire context |
271 | * |
272 | * Drop all locks currently held against this acquire context. |
273 | */ |
274 | void drm_modeset_drop_locks(struct drm_modeset_acquire_ctx *ctx) |
275 | { |
276 | if (WARN_ON(ctx->contended)) |
277 | __drm_stack_depot_print(stack_depot: ctx->stack_depot); |
278 | |
279 | while (!list_empty(head: &ctx->locked)) { |
280 | struct drm_modeset_lock *lock; |
281 | |
282 | lock = list_first_entry(&ctx->locked, |
283 | struct drm_modeset_lock, head); |
284 | |
285 | drm_modeset_unlock(lock); |
286 | } |
287 | } |
288 | EXPORT_SYMBOL(drm_modeset_drop_locks); |
289 | |
290 | static inline int modeset_lock(struct drm_modeset_lock *lock, |
291 | struct drm_modeset_acquire_ctx *ctx, |
292 | bool interruptible, bool slow) |
293 | { |
294 | int ret; |
295 | |
296 | if (WARN_ON(ctx->contended)) |
297 | __drm_stack_depot_print(stack_depot: ctx->stack_depot); |
298 | |
299 | if (ctx->trylock_only) { |
300 | lockdep_assert_held(&ctx->ww_ctx); |
301 | |
302 | if (!ww_mutex_trylock(lock: &lock->mutex, NULL)) |
303 | return -EBUSY; |
304 | else |
305 | return 0; |
306 | } else if (interruptible && slow) { |
307 | ret = ww_mutex_lock_slow_interruptible(lock: &lock->mutex, ctx: &ctx->ww_ctx); |
308 | } else if (interruptible) { |
309 | ret = ww_mutex_lock_interruptible(lock: &lock->mutex, ctx: &ctx->ww_ctx); |
310 | } else if (slow) { |
311 | ww_mutex_lock_slow(lock: &lock->mutex, ctx: &ctx->ww_ctx); |
312 | ret = 0; |
313 | } else { |
314 | ret = ww_mutex_lock(lock: &lock->mutex, ctx: &ctx->ww_ctx); |
315 | } |
316 | if (!ret) { |
317 | WARN_ON(!list_empty(&lock->head)); |
318 | list_add(new: &lock->head, head: &ctx->locked); |
319 | } else if (ret == -EALREADY) { |
320 | /* we already hold the lock.. this is fine. For atomic |
321 | * we will need to be able to drm_modeset_lock() things |
322 | * without having to keep track of what is already locked |
323 | * or not. |
324 | */ |
325 | ret = 0; |
326 | } else if (ret == -EDEADLK) { |
327 | ctx->contended = lock; |
328 | ctx->stack_depot = __drm_stack_depot_save(); |
329 | } |
330 | |
331 | return ret; |
332 | } |
333 | |
334 | /** |
335 | * drm_modeset_backoff - deadlock avoidance backoff |
336 | * @ctx: the acquire context |
337 | * |
338 | * If deadlock is detected (ie. drm_modeset_lock() returns -EDEADLK), |
339 | * you must call this function to drop all currently held locks and |
340 | * block until the contended lock becomes available. |
341 | * |
342 | * This function returns 0 on success, or -ERESTARTSYS if this context |
343 | * is initialized with %DRM_MODESET_ACQUIRE_INTERRUPTIBLE and the |
344 | * wait has been interrupted. |
345 | */ |
346 | int drm_modeset_backoff(struct drm_modeset_acquire_ctx *ctx) |
347 | { |
348 | struct drm_modeset_lock *contended = ctx->contended; |
349 | |
350 | ctx->contended = NULL; |
351 | ctx->stack_depot = 0; |
352 | |
353 | if (WARN_ON(!contended)) |
354 | return 0; |
355 | |
356 | drm_modeset_drop_locks(ctx); |
357 | |
358 | return modeset_lock(lock: contended, ctx, interruptible: ctx->interruptible, slow: true); |
359 | } |
360 | EXPORT_SYMBOL(drm_modeset_backoff); |
361 | |
362 | /** |
363 | * drm_modeset_lock_init - initialize lock |
364 | * @lock: lock to init |
365 | */ |
366 | void drm_modeset_lock_init(struct drm_modeset_lock *lock) |
367 | { |
368 | ww_mutex_init(lock: &lock->mutex, ww_class: &crtc_ww_class); |
369 | INIT_LIST_HEAD(list: &lock->head); |
370 | __drm_stack_depot_init(); |
371 | } |
372 | EXPORT_SYMBOL(drm_modeset_lock_init); |
373 | |
374 | /** |
375 | * drm_modeset_lock - take modeset lock |
376 | * @lock: lock to take |
377 | * @ctx: acquire ctx |
378 | * |
379 | * If @ctx is not NULL, then its ww acquire context is used and the |
380 | * lock will be tracked by the context and can be released by calling |
381 | * drm_modeset_drop_locks(). If -EDEADLK is returned, this means a |
382 | * deadlock scenario has been detected and it is an error to attempt |
383 | * to take any more locks without first calling drm_modeset_backoff(). |
384 | * |
385 | * If the @ctx is not NULL and initialized with |
386 | * %DRM_MODESET_ACQUIRE_INTERRUPTIBLE, this function will fail with |
387 | * -ERESTARTSYS when interrupted. |
388 | * |
389 | * If @ctx is NULL then the function call behaves like a normal, |
390 | * uninterruptible non-nesting mutex_lock() call. |
391 | */ |
392 | int drm_modeset_lock(struct drm_modeset_lock *lock, |
393 | struct drm_modeset_acquire_ctx *ctx) |
394 | { |
395 | if (ctx) |
396 | return modeset_lock(lock, ctx, interruptible: ctx->interruptible, slow: false); |
397 | |
398 | ww_mutex_lock(lock: &lock->mutex, NULL); |
399 | return 0; |
400 | } |
401 | EXPORT_SYMBOL(drm_modeset_lock); |
402 | |
403 | /** |
404 | * drm_modeset_lock_single_interruptible - take a single modeset lock |
405 | * @lock: lock to take |
406 | * |
407 | * This function behaves as drm_modeset_lock() with a NULL context, |
408 | * but performs interruptible waits. |
409 | * |
410 | * This function returns 0 on success, or -ERESTARTSYS when interrupted. |
411 | */ |
412 | int drm_modeset_lock_single_interruptible(struct drm_modeset_lock *lock) |
413 | { |
414 | return ww_mutex_lock_interruptible(lock: &lock->mutex, NULL); |
415 | } |
416 | EXPORT_SYMBOL(drm_modeset_lock_single_interruptible); |
417 | |
418 | /** |
419 | * drm_modeset_unlock - drop modeset lock |
420 | * @lock: lock to release |
421 | */ |
422 | void drm_modeset_unlock(struct drm_modeset_lock *lock) |
423 | { |
424 | list_del_init(entry: &lock->head); |
425 | ww_mutex_unlock(lock: &lock->mutex); |
426 | } |
427 | EXPORT_SYMBOL(drm_modeset_unlock); |
428 | |
429 | /** |
430 | * drm_modeset_lock_all_ctx - take all modeset locks |
431 | * @dev: DRM device |
432 | * @ctx: lock acquisition context |
433 | * |
434 | * This function takes all modeset locks, suitable where a more fine-grained |
435 | * scheme isn't (yet) implemented. |
436 | * |
437 | * Unlike drm_modeset_lock_all(), it doesn't take the &drm_mode_config.mutex |
438 | * since that lock isn't required for modeset state changes. Callers which |
439 | * need to grab that lock too need to do so outside of the acquire context |
440 | * @ctx. |
441 | * |
442 | * Locks acquired with this function should be released by calling the |
443 | * drm_modeset_drop_locks() function on @ctx. |
444 | * |
445 | * See also: DRM_MODESET_LOCK_ALL_BEGIN() and DRM_MODESET_LOCK_ALL_END() |
446 | * |
447 | * Returns: 0 on success or a negative error-code on failure. |
448 | */ |
449 | int drm_modeset_lock_all_ctx(struct drm_device *dev, |
450 | struct drm_modeset_acquire_ctx *ctx) |
451 | { |
452 | struct drm_private_obj *privobj; |
453 | struct drm_crtc *crtc; |
454 | struct drm_plane *plane; |
455 | int ret; |
456 | |
457 | ret = drm_modeset_lock(&dev->mode_config.connection_mutex, ctx); |
458 | if (ret) |
459 | return ret; |
460 | |
461 | drm_for_each_crtc(crtc, dev) { |
462 | ret = drm_modeset_lock(&crtc->mutex, ctx); |
463 | if (ret) |
464 | return ret; |
465 | } |
466 | |
467 | drm_for_each_plane(plane, dev) { |
468 | ret = drm_modeset_lock(&plane->mutex, ctx); |
469 | if (ret) |
470 | return ret; |
471 | } |
472 | |
473 | drm_for_each_privobj(privobj, dev) { |
474 | ret = drm_modeset_lock(&privobj->lock, ctx); |
475 | if (ret) |
476 | return ret; |
477 | } |
478 | |
479 | return 0; |
480 | } |
481 | EXPORT_SYMBOL(drm_modeset_lock_all_ctx); |
482 | |