diff options
author | Maarten Lankhorst <maarten.lankhorst@linux.intel.com> | 2017-09-12 09:37:44 -0400 |
---|---|---|
committer | Maarten Lankhorst <maarten.lankhorst@linux.intel.com> | 2017-09-13 03:50:52 -0400 |
commit | 6f8bcc744aad50d719845e4ce06a7831e96e1109 (patch) | |
tree | b80456af140c02f8b25c778bd01f54acc85df2b5 | |
parent | 9ab12e88a0b46b4e6fa32bb0a2875c813a928e73 (diff) |
drm/atomic: Prepare drm_modeset_lock infrastructure for interruptible waiting, v2.
When we want to make drm_atomic_commit interruptible, there are a lot of
places that call the lock function, which we don't have control over.
Rather than trying to convert every single one, it's easier to toggle
interruptible waiting per acquire_ctx. If drm_modeset_acquire_init is
called with DRM_MODESET_ACQUIRE_INTERRUPTIBLE, then we will perform
interruptible waits in drm_modeset_lock and drm_modeset_backoff.
Changes since v1:
- Fix locking example in drm_modeset_lock.c to be compatible
with interruptible waiting (xexaxo) and make it default.
Uninterruptible waiting shouldn't happen except in corner cases,
but the example will still apply if the flag is removed.
- Add drm_modeset_lock_single_interruptible() to documentation.
- Fix dead link to removed drm_modeset_lock_interruptible() in
drm_modeset_lock().
Signed-off-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch> #v1
Cc: Emil Velikov <emil.l.velikov@gmail.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20170912133749.6532-2-maarten.lankhorst@linux.intel.com
Reviewed-by: Emil Velikov <emil.l.velikov@gmail.com>
-rw-r--r-- | drivers/gpu/drm/drm_debugfs_crc.c | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/drm_modeset_lock.c | 96 | ||||
-rw-r--r-- | include/drm/drm_modeset_lock.h | 12 |
3 files changed, 57 insertions, 53 deletions
diff --git a/drivers/gpu/drm/drm_debugfs_crc.c b/drivers/gpu/drm/drm_debugfs_crc.c index f9e26dda56d6..9dd879589a2c 100644 --- a/drivers/gpu/drm/drm_debugfs_crc.c +++ b/drivers/gpu/drm/drm_debugfs_crc.c | |||
@@ -155,7 +155,7 @@ static int crtc_crc_open(struct inode *inode, struct file *filep) | |||
155 | int ret = 0; | 155 | int ret = 0; |
156 | 156 | ||
157 | if (drm_drv_uses_atomic_modeset(crtc->dev)) { | 157 | if (drm_drv_uses_atomic_modeset(crtc->dev)) { |
158 | ret = drm_modeset_lock_interruptible(&crtc->mutex, NULL); | 158 | ret = drm_modeset_lock_single_interruptible(&crtc->mutex); |
159 | if (ret) | 159 | if (ret) |
160 | return ret; | 160 | return ret; |
161 | 161 | ||
diff --git a/drivers/gpu/drm/drm_modeset_lock.c b/drivers/gpu/drm/drm_modeset_lock.c index af4e906c630d..e123497da0ca 100644 --- a/drivers/gpu/drm/drm_modeset_lock.c +++ b/drivers/gpu/drm/drm_modeset_lock.c | |||
@@ -39,23 +39,28 @@ | |||
39 | * | 39 | * |
40 | * The basic usage pattern is to:: | 40 | * The basic usage pattern is to:: |
41 | * | 41 | * |
42 | * drm_modeset_acquire_init(&ctx) | 42 | * drm_modeset_acquire_init(ctx, DRM_MODESET_ACQUIRE_INTERRUPTIBLE) |
43 | * retry: | 43 | * retry: |
44 | * foreach (lock in random_ordered_set_of_locks) { | 44 | * foreach (lock in random_ordered_set_of_locks) { |
45 | * ret = drm_modeset_lock(lock, &ctx) | 45 | * ret = drm_modeset_lock(lock, ctx) |
46 | * if (ret == -EDEADLK) { | 46 | * if (ret == -EDEADLK) { |
47 | * drm_modeset_backoff(&ctx); | 47 | * ret = drm_modeset_backoff(ctx); |
48 | * goto retry; | 48 | * if (!ret) |
49 | * goto retry; | ||
49 | * } | 50 | * } |
51 | * if (ret) | ||
52 | * goto out; | ||
50 | * } | 53 | * } |
51 | * ... do stuff ... | 54 | * ... do stuff ... |
52 | * drm_modeset_drop_locks(&ctx); | 55 | * out: |
53 | * drm_modeset_acquire_fini(&ctx); | 56 | * drm_modeset_drop_locks(ctx); |
57 | * drm_modeset_acquire_fini(ctx); | ||
54 | * | 58 | * |
55 | * If all that is needed is a single modeset lock, then the &struct | 59 | * If all that is needed is a single modeset lock, then the &struct |
56 | * drm_modeset_acquire_ctx is not needed and the locking can be simplified | 60 | * drm_modeset_acquire_ctx is not needed and the locking can be simplified |
57 | * by passing a NULL instead of ctx in the drm_modeset_lock() | 61 | * by passing a NULL instead of ctx in the drm_modeset_lock() call or |
58 | * call and, when done, by calling drm_modeset_unlock(). | 62 | * calling drm_modeset_lock_single_interruptible(). To unlock afterwards |
63 | * call drm_modeset_unlock(). | ||
59 | * | 64 | * |
60 | * On top of these per-object locks using &ww_mutex there's also an overall | 65 | * On top of these per-object locks using &ww_mutex there's also an overall |
61 | * &drm_mode_config.mutex, for protecting everything else. Mostly this means | 66 | * &drm_mode_config.mutex, for protecting everything else. Mostly this means |
@@ -178,7 +183,11 @@ EXPORT_SYMBOL(drm_warn_on_modeset_not_all_locked); | |||
178 | /** | 183 | /** |
179 | * drm_modeset_acquire_init - initialize acquire context | 184 | * drm_modeset_acquire_init - initialize acquire context |
180 | * @ctx: the acquire context | 185 | * @ctx: the acquire context |
181 | * @flags: for future | 186 | * @flags: 0 or %DRM_MODESET_ACQUIRE_INTERRUPTIBLE |
187 | * | ||
188 | * When passing %DRM_MODESET_ACQUIRE_INTERRUPTIBLE to @flags, | ||
189 | * all calls to drm_modeset_lock() will perform an interruptible | ||
190 | * wait. | ||
182 | */ | 191 | */ |
183 | void drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx, | 192 | void drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx, |
184 | uint32_t flags) | 193 | uint32_t flags) |
@@ -186,6 +195,9 @@ void drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx, | |||
186 | memset(ctx, 0, sizeof(*ctx)); | 195 | memset(ctx, 0, sizeof(*ctx)); |
187 | ww_acquire_init(&ctx->ww_ctx, &crtc_ww_class); | 196 | ww_acquire_init(&ctx->ww_ctx, &crtc_ww_class); |
188 | INIT_LIST_HEAD(&ctx->locked); | 197 | INIT_LIST_HEAD(&ctx->locked); |
198 | |||
199 | if (flags & DRM_MODESET_ACQUIRE_INTERRUPTIBLE) | ||
200 | ctx->interruptible = true; | ||
189 | } | 201 | } |
190 | EXPORT_SYMBOL(drm_modeset_acquire_init); | 202 | EXPORT_SYMBOL(drm_modeset_acquire_init); |
191 | 203 | ||
@@ -261,8 +273,19 @@ static inline int modeset_lock(struct drm_modeset_lock *lock, | |||
261 | return ret; | 273 | return ret; |
262 | } | 274 | } |
263 | 275 | ||
264 | static int modeset_backoff(struct drm_modeset_acquire_ctx *ctx, | 276 | /** |
265 | bool interruptible) | 277 | * drm_modeset_backoff - deadlock avoidance backoff |
278 | * @ctx: the acquire context | ||
279 | * | ||
280 | * If deadlock is detected (ie. drm_modeset_lock() returns -EDEADLK), | ||
281 | * you must call this function to drop all currently held locks and | ||
282 | * block until the contended lock becomes available. | ||
283 | * | ||
284 | * This function returns 0 on success, or -ERESTARTSYS if this context | ||
285 | * is initialized with %DRM_MODESET_ACQUIRE_INTERRUPTIBLE and the | ||
286 | * wait has been interrupted. | ||
287 | */ | ||
288 | int drm_modeset_backoff(struct drm_modeset_acquire_ctx *ctx) | ||
266 | { | 289 | { |
267 | struct drm_modeset_lock *contended = ctx->contended; | 290 | struct drm_modeset_lock *contended = ctx->contended; |
268 | 291 | ||
@@ -273,36 +296,11 @@ static int modeset_backoff(struct drm_modeset_acquire_ctx *ctx, | |||
273 | 296 | ||
274 | drm_modeset_drop_locks(ctx); | 297 | drm_modeset_drop_locks(ctx); |
275 | 298 | ||
276 | return modeset_lock(contended, ctx, interruptible, true); | 299 | return modeset_lock(contended, ctx, ctx->interruptible, true); |
277 | } | ||
278 | |||
279 | /** | ||
280 | * drm_modeset_backoff - deadlock avoidance backoff | ||
281 | * @ctx: the acquire context | ||
282 | * | ||
283 | * If deadlock is detected (ie. drm_modeset_lock() returns -EDEADLK), | ||
284 | * you must call this function to drop all currently held locks and | ||
285 | * block until the contended lock becomes available. | ||
286 | */ | ||
287 | void drm_modeset_backoff(struct drm_modeset_acquire_ctx *ctx) | ||
288 | { | ||
289 | modeset_backoff(ctx, false); | ||
290 | } | 300 | } |
291 | EXPORT_SYMBOL(drm_modeset_backoff); | 301 | EXPORT_SYMBOL(drm_modeset_backoff); |
292 | 302 | ||
293 | /** | 303 | /** |
294 | * drm_modeset_backoff_interruptible - deadlock avoidance backoff | ||
295 | * @ctx: the acquire context | ||
296 | * | ||
297 | * Interruptible version of drm_modeset_backoff() | ||
298 | */ | ||
299 | int drm_modeset_backoff_interruptible(struct drm_modeset_acquire_ctx *ctx) | ||
300 | { | ||
301 | return modeset_backoff(ctx, true); | ||
302 | } | ||
303 | EXPORT_SYMBOL(drm_modeset_backoff_interruptible); | ||
304 | |||
305 | /** | ||
306 | * drm_modeset_lock_init - initialize lock | 304 | * drm_modeset_lock_init - initialize lock |
307 | * @lock: lock to init | 305 | * @lock: lock to init |
308 | */ | 306 | */ |
@@ -324,14 +322,18 @@ EXPORT_SYMBOL(drm_modeset_lock_init); | |||
324 | * deadlock scenario has been detected and it is an error to attempt | 322 | * deadlock scenario has been detected and it is an error to attempt |
325 | * to take any more locks without first calling drm_modeset_backoff(). | 323 | * to take any more locks without first calling drm_modeset_backoff(). |
326 | * | 324 | * |
325 | * If the @ctx is not NULL and initialized with | ||
326 | * %DRM_MODESET_ACQUIRE_INTERRUPTIBLE, this function will fail with | ||
327 | * -ERESTARTSYS when interrupted. | ||
328 | * | ||
327 | * If @ctx is NULL then the function call behaves like a normal, | 329 | * If @ctx is NULL then the function call behaves like a normal, |
328 | * non-nesting mutex_lock() call. | 330 | * uninterruptible non-nesting mutex_lock() call. |
329 | */ | 331 | */ |
330 | int drm_modeset_lock(struct drm_modeset_lock *lock, | 332 | int drm_modeset_lock(struct drm_modeset_lock *lock, |
331 | struct drm_modeset_acquire_ctx *ctx) | 333 | struct drm_modeset_acquire_ctx *ctx) |
332 | { | 334 | { |
333 | if (ctx) | 335 | if (ctx) |
334 | return modeset_lock(lock, ctx, false, false); | 336 | return modeset_lock(lock, ctx, ctx->interruptible, false); |
335 | 337 | ||
336 | ww_mutex_lock(&lock->mutex, NULL); | 338 | ww_mutex_lock(&lock->mutex, NULL); |
337 | return 0; | 339 | return 0; |
@@ -339,21 +341,19 @@ int drm_modeset_lock(struct drm_modeset_lock *lock, | |||
339 | EXPORT_SYMBOL(drm_modeset_lock); | 341 | EXPORT_SYMBOL(drm_modeset_lock); |
340 | 342 | ||
341 | /** | 343 | /** |
342 | * drm_modeset_lock_interruptible - take modeset lock | 344 | * drm_modeset_lock_single_interruptible - take a single modeset lock |
343 | * @lock: lock to take | 345 | * @lock: lock to take |
344 | * @ctx: acquire ctx | ||
345 | * | 346 | * |
346 | * Interruptible version of drm_modeset_lock() | 347 | * This function behaves as drm_modeset_lock() with a NULL context, |
348 | * but performs interruptible waits. | ||
349 | * | ||
350 | * This function returns 0 on success, or -ERESTARTSYS when interrupted. | ||
347 | */ | 351 | */ |
348 | int drm_modeset_lock_interruptible(struct drm_modeset_lock *lock, | 352 | int drm_modeset_lock_single_interruptible(struct drm_modeset_lock *lock) |
349 | struct drm_modeset_acquire_ctx *ctx) | ||
350 | { | 353 | { |
351 | if (ctx) | ||
352 | return modeset_lock(lock, ctx, true, false); | ||
353 | |||
354 | return ww_mutex_lock_interruptible(&lock->mutex, NULL); | 354 | return ww_mutex_lock_interruptible(&lock->mutex, NULL); |
355 | } | 355 | } |
356 | EXPORT_SYMBOL(drm_modeset_lock_interruptible); | 356 | EXPORT_SYMBOL(drm_modeset_lock_single_interruptible); |
357 | 357 | ||
358 | /** | 358 | /** |
359 | * drm_modeset_unlock - drop modeset lock | 359 | * drm_modeset_unlock - drop modeset lock |
diff --git a/include/drm/drm_modeset_lock.h b/include/drm/drm_modeset_lock.h index 4b27c2bb955c..a685d1bb21f2 100644 --- a/include/drm/drm_modeset_lock.h +++ b/include/drm/drm_modeset_lock.h | |||
@@ -34,6 +34,7 @@ struct drm_modeset_lock; | |||
34 | * @contended: used internally for -EDEADLK handling | 34 | * @contended: used internally for -EDEADLK handling |
35 | * @locked: list of held locks | 35 | * @locked: list of held locks |
36 | * @trylock_only: trylock mode used in atomic contexts/panic notifiers | 36 | * @trylock_only: trylock mode used in atomic contexts/panic notifiers |
37 | * @interruptible: whether interruptible locking should be used. | ||
37 | * | 38 | * |
38 | * Each thread competing for a set of locks must use one acquire | 39 | * Each thread competing for a set of locks must use one acquire |
39 | * ctx. And if any lock fxn returns -EDEADLK, it must backoff and | 40 | * ctx. And if any lock fxn returns -EDEADLK, it must backoff and |
@@ -59,6 +60,9 @@ struct drm_modeset_acquire_ctx { | |||
59 | * Trylock mode, use only for panic handlers! | 60 | * Trylock mode, use only for panic handlers! |
60 | */ | 61 | */ |
61 | bool trylock_only; | 62 | bool trylock_only; |
63 | |||
64 | /* Perform interruptible waits on this context. */ | ||
65 | bool interruptible; | ||
62 | }; | 66 | }; |
63 | 67 | ||
64 | /** | 68 | /** |
@@ -82,12 +86,13 @@ struct drm_modeset_lock { | |||
82 | struct list_head head; | 86 | struct list_head head; |
83 | }; | 87 | }; |
84 | 88 | ||
89 | #define DRM_MODESET_ACQUIRE_INTERRUPTIBLE BIT(0) | ||
90 | |||
85 | void drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx, | 91 | void drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx, |
86 | uint32_t flags); | 92 | uint32_t flags); |
87 | void drm_modeset_acquire_fini(struct drm_modeset_acquire_ctx *ctx); | 93 | void drm_modeset_acquire_fini(struct drm_modeset_acquire_ctx *ctx); |
88 | void drm_modeset_drop_locks(struct drm_modeset_acquire_ctx *ctx); | 94 | void drm_modeset_drop_locks(struct drm_modeset_acquire_ctx *ctx); |
89 | void drm_modeset_backoff(struct drm_modeset_acquire_ctx *ctx); | 95 | int drm_modeset_backoff(struct drm_modeset_acquire_ctx *ctx); |
90 | int drm_modeset_backoff_interruptible(struct drm_modeset_acquire_ctx *ctx); | ||
91 | 96 | ||
92 | void drm_modeset_lock_init(struct drm_modeset_lock *lock); | 97 | void drm_modeset_lock_init(struct drm_modeset_lock *lock); |
93 | 98 | ||
@@ -111,8 +116,7 @@ static inline bool drm_modeset_is_locked(struct drm_modeset_lock *lock) | |||
111 | 116 | ||
112 | int drm_modeset_lock(struct drm_modeset_lock *lock, | 117 | int drm_modeset_lock(struct drm_modeset_lock *lock, |
113 | struct drm_modeset_acquire_ctx *ctx); | 118 | struct drm_modeset_acquire_ctx *ctx); |
114 | int drm_modeset_lock_interruptible(struct drm_modeset_lock *lock, | 119 | int __must_check drm_modeset_lock_single_interruptible(struct drm_modeset_lock *lock); |
115 | struct drm_modeset_acquire_ctx *ctx); | ||
116 | void drm_modeset_unlock(struct drm_modeset_lock *lock); | 120 | void drm_modeset_unlock(struct drm_modeset_lock *lock); |
117 | 121 | ||
118 | struct drm_device; | 122 | struct drm_device; |