From: François Tigeot Date: Thu, 24 Dec 2015 08:56:57 +0000 (+0100) Subject: drm: Fix ww mutex crtc locking X-Git-Tag: v4.6.0rc~1131 X-Git-Url: https://gitweb.dragonflybsd.org/~tuxillo/dragonfly.git/commitdiff_plain/4a1f0d757dc89c9bbe3a5b0e16c28eb180939912 drm: Fix ww mutex crtc locking * The original code had many problems: - the ctx wasn't available to drm_modeset_unlock(). - 'struct list_head' was used for both the list head AND the list entry It's like it was coded to create maximum confusion on top of maximum pain * drm_modeset_acquire_ctx structure needs a second list which we add kmalloc()'d entries to to track the extra locks in the EALREADY case * Replace the reference from the modeset_lock structure with a pointer to an intermediate (kmalloc'd) structure that references both and turn the list entry in the lock into a list head Submitted-by: Matthew Dillon --- diff --git a/sys/dev/drm/drm_modeset_lock.c b/sys/dev/drm/drm_modeset_lock.c index 0a71b8f3c5..ddb9f8e06c 100644 --- a/sys/dev/drm/drm_modeset_lock.c +++ b/sys/dev/drm/drm_modeset_lock.c @@ -271,12 +271,12 @@ void drm_modeset_drop_locks(struct drm_modeset_acquire_ctx *ctx) { WARN_ON(ctx->contended); while (!list_empty(&ctx->locked)) { - struct drm_modeset_lock *lock; + struct drm_modeset_lock_info *info; - lock = list_first_entry(&ctx->locked, - struct drm_modeset_lock, head); + info = list_first_entry(&ctx->locked, + struct drm_modeset_lock_info, ctx_entry); - drm_modeset_unlock(lock); + drm_modeset_unlock(info->lock); } } EXPORT_SYMBOL(drm_modeset_drop_locks); @@ -299,10 +299,7 @@ static inline int modeset_lock(struct drm_modeset_lock *lock, } else { ret = ww_mutex_lock(&lock->mutex, &ctx->ww_ctx); } - if (!ret) { - WARN_ON(!list_empty(&lock->head)); - list_add(&lock->head, &ctx->locked); - } else if (ret == -EALREADY) { + if (ret == -EALREADY) { /* we already hold the lock.. this is fine. For atomic * we will need to be able to drm_modeset_lock() things * without having to keep track of what is already locked @@ -312,6 +309,17 @@ static inline int modeset_lock(struct drm_modeset_lock *lock, } else if (ret == -EDEADLK) { ctx->contended = lock; } + if (ret == 0) { + struct drm_modeset_lock_info *info; + + info = kzalloc(sizeof(*info), GFP_KERNEL); + INIT_LIST_HEAD(&info->ctx_entry); + INIT_LIST_HEAD(&info->lock_entry); + info->lock = lock; + info->ctx = ctx; + list_add(&info->ctx_entry, &ctx->locked); + list_add(&info->lock_entry, &lock->locked); + } return ret; } @@ -402,7 +410,17 @@ EXPORT_SYMBOL(drm_modeset_lock_interruptible); */ void drm_modeset_unlock(struct drm_modeset_lock *lock) { - list_del_init(&lock->head); + struct drm_modeset_lock_info *info; + + /* undo in reverse order */ + if (!list_empty(&lock->locked)) { + info = list_last_entry(&lock->locked, + struct drm_modeset_lock_info, lock_entry); + list_del_init(&info->lock_entry); + if (info->ctx) + list_del_init(&info->ctx_entry); + kfree(info); + } ww_mutex_unlock(&lock->mutex); } EXPORT_SYMBOL(drm_modeset_unlock); diff --git a/sys/dev/drm/include/drm/drm_modeset_lock.h b/sys/dev/drm/include/drm/drm_modeset_lock.h index 1e88695784..d41f7dc73a 100644 --- a/sys/dev/drm/include/drm/drm_modeset_lock.h +++ b/sys/dev/drm/include/drm/drm_modeset_lock.h @@ -50,7 +50,7 @@ struct drm_modeset_acquire_ctx { struct drm_modeset_lock *contended; /** - * list of held locks (drm_modeset_lock) + * list of held locks (drm_modeset_lock_info) */ struct list_head locked; @@ -60,6 +60,16 @@ struct drm_modeset_acquire_ctx { bool trylock_only; }; +/* + * Keep track of extra locks. + */ +struct drm_modeset_lock_info { + struct list_head ctx_entry; + struct list_head lock_entry; + struct drm_modeset_lock *lock; + struct drm_modeset_acquire_ctx *ctx; +}; + /** * struct drm_modeset_lock - used for locking modeset resources. * @mutex: resource locking @@ -78,7 +88,7 @@ struct drm_modeset_lock { * Resources that are locked as part of an atomic update are added * to a list (so we know what to unlock at the end). */ - struct list_head head; + struct list_head locked; }; extern struct ww_class crtc_ww_class; @@ -97,7 +107,7 @@ int drm_modeset_backoff_interruptible(struct drm_modeset_acquire_ctx *ctx); static inline void drm_modeset_lock_init(struct drm_modeset_lock *lock) { ww_mutex_init(&lock->mutex, &crtc_ww_class); - INIT_LIST_HEAD(&lock->head); + INIT_LIST_HEAD(&lock->locked); } /** @@ -106,7 +116,7 @@ static inline void drm_modeset_lock_init(struct drm_modeset_lock *lock) */ static inline void drm_modeset_lock_fini(struct drm_modeset_lock *lock) { - WARN_ON(!list_empty(&lock->head)); + WARN_ON(!list_empty(&lock->locked)); } /**