This should be squashed into the relevant later patches that start to use this. Signed-off-by: Daniel Vetter <daniel.vetter@xxxxxxxx> --- drivers/gpu/drm/drm_crtc.c | 4 +- drivers/gpu/drm/drm_modeset_lock.c | 72 ++++++++++++++++++++++++------------ drivers/gpu/drm/i915/intel_display.c | 3 +- include/drm/drm_modeset_lock.h | 41 +++++++++++++++++++- include/uapi/drm/drm_mode.h | 3 ++ 5 files changed, 95 insertions(+), 28 deletions(-) diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 9e826aa5fc5c..cf622f6de07c 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -39,7 +39,6 @@ #include <drm/drm_fourcc.h> #include <drm/drm_modeset_lock.h> -#include "drm_crtc_internal.h" /** * drm_modeset_lock_all - take all modeset locks @@ -61,7 +60,7 @@ void drm_modeset_lock_all(struct drm_device *dev) mutex_lock(&config->mutex); - drm_modeset_acquire_init(ctx); + drm_modeset_acquire_init(ctx, false, false); retry: ret = drm_modeset_lock(&config->connection_mutex, ctx); @@ -106,6 +105,7 @@ void drm_modeset_unlock_all(struct drm_device *dev) config->acquire_ctx = NULL; drm_modeset_drop_locks(ctx); + ww_acquire_fini(&ctx->ww_ctx); drm_modeset_acquire_fini(ctx); kfree(ctx); diff --git a/drivers/gpu/drm/drm_modeset_lock.c b/drivers/gpu/drm/drm_modeset_lock.c index 81607ccebd89..6580d6ce8f63 100644 --- a/drivers/gpu/drm/drm_modeset_lock.c +++ b/drivers/gpu/drm/drm_modeset_lock.c @@ -26,21 +26,32 @@ #include <drm/drm_modeset_lock.h> -void drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx) +void drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx, + bool nolock, bool nonblock) { ww_acquire_init(&ctx->ww_ctx, &crtc_ww_class); INIT_LIST_HEAD(&ctx->locked); + mutex_init(&ctx->mutex); + ctx->nolock = nolock; + ctx->nonblock = nonblock; } EXPORT_SYMBOL(drm_modeset_acquire_init); void drm_modeset_acquire_fini(struct drm_modeset_acquire_ctx *ctx) { - ww_acquire_fini(&ctx->ww_ctx); + WARN_ON(ctx->contended); + /* + * NOTE: it is intentional that ww_acquire_fini() is not called + * here.. due to the way lock handover works in drm_atomic + */ + mutex_destroy(&ctx->mutex); } EXPORT_SYMBOL(drm_modeset_acquire_fini); void drm_modeset_drop_locks(struct drm_modeset_acquire_ctx *ctx) { + WARN_ON(ctx->contended); + mutex_lock(&ctx->mutex); while (!list_empty(&ctx->locked)) { struct drm_modeset_lock *lock; @@ -49,31 +60,45 @@ void drm_modeset_drop_locks(struct drm_modeset_acquire_ctx *ctx) drm_modeset_unlock(lock); } + mutex_unlock(&ctx->mutex); } EXPORT_SYMBOL(drm_modeset_drop_locks); static int modeset_lock(struct drm_modeset_lock *lock, - struct drm_modeset_acquire_ctx *ctx, - bool interruptible) + struct drm_modeset_acquire_ctx *ctx, + bool interruptible, bool slow) { int ret; + if (ctx->nolock) + return 0; + + WARN_ON(ctx->frozen); /* all locks should be held by now! */ + WARN_ON(ctx->contended); + +retry: if (interruptible) { ret = ww_mutex_lock_interruptible(&lock->mutex, &ctx->ww_ctx); + } else if (slow) { + ww_mutex_lock_slow(&lock->mutex, &ctx->ww_ctx); + ret = 0; } else { ret = ww_mutex_lock(&lock->mutex, &ctx->ww_ctx); } - - if (ret == 0) { + if (!ret) { + if (lock->atomic_pending) { + /* some other pending update with dropped locks */ + ww_mutex_unlock(&lock->mutex); + if (ctx->nonblock) + return -EBUSY; + wait_event(lock->event, !lock->atomic_pending); + goto retry; + } + lock->atomic_pending = true; + WARN_ON(!list_empty(&lock->head)); list_add(&lock->head, &ctx->locked); } else if (ret == -EALREADY) { - /* - * We already hold the lock. This is only fine if it's the lock - * we've contended on. - */ - WARN_ON(ctx->contended != lock); - ctx->contended = NULL; - + /* we already hold the lock.. this is fine */ ret = 0; } else if (ret == -EDEADLK) { ctx->contended = lock; @@ -84,19 +109,18 @@ static int modeset_lock(struct drm_modeset_lock *lock, void drm_modeset_backoff(struct drm_modeset_acquire_ctx *ctx) { - drm_modeset_drop_locks(ctx); + struct drm_modeset_lock *contended = ctx->contended; - ww_mutex_lock_slow(&ctx->contended->mutex, ctx); -} -EXPORT_SYMBOL(drm_modeset_backoff); + ctx->contended = NULL; + + if (WARN_ON(!contended)) + return; -void drm_modeset_backoff_interruptible(struct drm_modeset_acquire_ctx *ctx) -{ drm_modeset_drop_locks(ctx); - ww_mutex_lock_slow(&ctx->contended->mutex, ctx); + modeset_lock(contended, ctx, false, true); } -EXPORT_SYMBOL(drm_modeset_backoff_interruptible); +EXPORT_SYMBOL(drm_modeset_backoff); /** * drm_modeset_lock - take modeset lock @@ -112,7 +136,7 @@ int drm_modeset_lock(struct drm_modeset_lock *lock, struct drm_modeset_acquire_ctx *ctx) { if (ctx) - return modeset_lock(lock, ctx, false); + return modeset_lock(lock, ctx, false, false); ww_mutex_lock(&lock->mutex, NULL); return 0; @@ -123,7 +147,7 @@ int drm_modeset_lock_interruptible(struct drm_modeset_lock *lock, struct drm_modeset_acquire_ctx *ctx) { if (ctx) - return modeset_lock(lock, ctx, true); + return modeset_lock(lock, ctx, true, false); return ww_mutex_lock_interruptible(&lock->mutex, NULL); } @@ -136,7 +160,9 @@ EXPORT_SYMBOL(drm_modeset_lock_interruptible); void drm_modeset_unlock(struct drm_modeset_lock *lock) { list_del_init(&lock->head); + lock->atomic_pending = false; ww_mutex_unlock(&lock->mutex); + wake_up_all(&lock->event); } EXPORT_SYMBOL(drm_modeset_unlock); diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 9d12e3e15dfa..ba6cbbbfe596 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -7990,7 +7990,7 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector, connector->base.id, drm_get_connector_name(connector), encoder->base.id, drm_get_encoder_name(encoder)); - drm_modeset_acquire_init(ctx); + drm_modeset_acquire_init(ctx, false, false); retry: ret = drm_modeset_lock(&config->connection_mutex, ctx); @@ -8103,6 +8103,7 @@ fail_unlock: } drm_modeset_drop_locks(ctx); + ww_acquire_fini(&ctx->ww_ctx); drm_modeset_acquire_fini(ctx); return false; diff --git a/include/drm/drm_modeset_lock.h b/include/drm/drm_modeset_lock.h index e67fb815b610..2630da3ef1b4 100644 --- a/include/drm/drm_modeset_lock.h +++ b/include/drm/drm_modeset_lock.h @@ -32,6 +32,15 @@ struct drm_modeset_acquire_ctx { struct ww_acquire_ctx ww_ctx; + bool nolock : 1; + bool nonblock : 1; + + /* just for debugging, the context is 'frozen' in drm_atomic_check() + * to catch anyone who might be trying to acquire a lock after it is + * too late. + */ + bool frozen : 1; + /* contended lock: if a lock is contended you should only call * drm_modeset_backoff() which drops locks and slow-locks the * contended lock. @@ -40,6 +49,16 @@ struct drm_modeset_acquire_ctx { /* list of 'struct drm_modeset_lock': */ struct list_head locked; + + /* currently simply for protecting against 'locked' list manipulation + * between original thread calling atomic->end() and driver thread + * calling back drm_atomic_commit_unlocked(). + * + * Other spots are sufficiently synchronized by virtue of holding + * the lock's ww_mutex. But during the lock/resource hand-over to the + * driver thread (drop_locks()/grab_locks()), we cannot rely on this. + */ + struct mutex mutex; }; /** @@ -59,24 +78,42 @@ struct drm_modeset_lock { struct ww_mutex mutex; /** + * Are we busy (pending asynchronous/NONBLOCK update)? Any further + * asynchronous update will return -EBUSY if it also needs to acquire + * this lock. While a synchronous update will block until the pending + * async update completes. + * + * Drivers must ensure the update is completed before sending vblank + * event to userspace. Typically this just means don't send event + * before drm_atomic_commit_unlocked() returns. + */ + bool atomic_pending; + + /** * 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; + + /** + * For waiting on atomic_pending locks, if not a NONBLOCK operation. + */ + wait_queue_head_t event; }; extern struct ww_class crtc_ww_class; -void drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx); +void drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx, + bool nolock, bool nonblock); void drm_modeset_acquire_fini(struct drm_modeset_acquire_ctx *ctx); void drm_modeset_drop_locks(struct drm_modeset_acquire_ctx *ctx); void drm_modeset_backoff(struct drm_modeset_acquire_ctx *ctx); -void 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_waitqueue_head(&lock->event); } static inline void drm_modeset_lock_fini(struct drm_modeset_lock *lock) diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h index ded505ede145..6421edcd27a8 100644 --- a/include/uapi/drm/drm_mode.h +++ b/include/uapi/drm/drm_mode.h @@ -511,4 +511,7 @@ struct drm_mode_destroy_dumb { uint32_t handle; }; +#define DRM_MODE_ATOMIC_NONBLOCK 0x0200 +#define DRM_MODE_ATOMIC_NOLOCK 0x8000 /* only used internally */ + #endif -- 1.9.2 _______________________________________________ dri-devel mailing list dri-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/dri-devel