aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/drm_modeset_lock.c
diff options
context:
space:
mode:
authorDaniel Vetter <daniel.vetter@ffwll.ch>2014-07-27 13:09:33 -0400
committerDaniel Vetter <daniel.vetter@ffwll.ch>2014-08-08 11:47:01 -0400
commitcb597bb3a2fbfc871cc1c703fb330d247bd21394 (patch)
tree4bedf2841031c958813a71d8358ec1aa6178a76d /drivers/gpu/drm/drm_modeset_lock.c
parent3d30a59bfcb7c96d4aacdb053c2ccc49394b2311 (diff)
drm: trylock modest locking for fbdev panics
In the fbdev code we want to do trylocks only to avoid deadlocks and other ugly issues. Thus far we've only grabbed the overall modeset lock, but that already failed to exclude a pile of potential concurrent operations. With proper atomic support this will be worse. So add a trylock mode to the modeset locking code which attempts all locks only with trylocks, if possible. We need to track this in the locking functions themselves and can't restrict this to drivers since driver-private w/w mutexes must be treated the same way. There's still the issue that other driver private locks aren't handled here at all, but well can't have everything. With this we will at least not regress, even once atomic allows lots of concurrent kms activity. Aside: We should move the acquire context to stack-based allocation in the callers to get rid of that awful WARN_ON(kmalloc_failed) control flow which just blows up when memory is short. But that's material for separate patches. v2: - Fix logic inversion fumble in the fb helper. - Add proper kerneldoc. Reviewed-by: Matt Roper <matthew.d.roper@intel.com> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Diffstat (limited to 'drivers/gpu/drm/drm_modeset_lock.c')
-rw-r--r--drivers/gpu/drm/drm_modeset_lock.c56
1 files changed, 44 insertions, 12 deletions
diff --git a/drivers/gpu/drm/drm_modeset_lock.c b/drivers/gpu/drm/drm_modeset_lock.c
index 4753c8bd5ab5..5280b64a0230 100644
--- a/drivers/gpu/drm/drm_modeset_lock.c
+++ b/drivers/gpu/drm/drm_modeset_lock.c
@@ -57,26 +57,37 @@
57 57
58 58
59/** 59/**
60 * drm_modeset_lock_all - take all modeset locks 60 * __drm_modeset_lock_all - internal helper to grab all modeset locks
61 * @dev: drm device 61 * @dev: DRM device
62 * @trylock: trylock mode for atomic contexts
62 * 63 *
63 * This function takes all modeset locks, suitable where a more fine-grained 64 * This is a special version of drm_modeset_lock_all() which can also be used in
64 * scheme isn't (yet) implemented. Locks must be dropped with 65 * atomic contexts. Then @trylock must be set to true.
65 * drm_modeset_unlock_all. 66 *
67 * Returns:
68 * 0 on success or negative error code on failure.
66 */ 69 */
67void drm_modeset_lock_all(struct drm_device *dev) 70int __drm_modeset_lock_all(struct drm_device *dev,
71 bool trylock)
68{ 72{
69 struct drm_mode_config *config = &dev->mode_config; 73 struct drm_mode_config *config = &dev->mode_config;
70 struct drm_modeset_acquire_ctx *ctx; 74 struct drm_modeset_acquire_ctx *ctx;
71 int ret; 75 int ret;
72 76
73 ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); 77 ctx = kzalloc(sizeof(*ctx),
74 if (WARN_ON(!ctx)) 78 trylock ? GFP_ATOMIC : GFP_KERNEL);
75 return; 79 if (!ctx)
80 return -ENOMEM;
76 81
77 mutex_lock(&config->mutex); 82 if (trylock) {
83 if (!mutex_trylock(&config->mutex))
84 return -EBUSY;
85 } else {
86 mutex_lock(&config->mutex);
87 }
78 88
79 drm_modeset_acquire_init(ctx, 0); 89 drm_modeset_acquire_init(ctx, 0);
90 ctx->trylock_only = trylock;
80 91
81retry: 92retry:
82 ret = drm_modeset_lock(&config->connection_mutex, ctx); 93 ret = drm_modeset_lock(&config->connection_mutex, ctx);
@@ -95,13 +106,29 @@ retry:
95 106
96 drm_warn_on_modeset_not_all_locked(dev); 107 drm_warn_on_modeset_not_all_locked(dev);
97 108
98 return; 109 return 0;
99 110
100fail: 111fail:
101 if (ret == -EDEADLK) { 112 if (ret == -EDEADLK) {
102 drm_modeset_backoff(ctx); 113 drm_modeset_backoff(ctx);
103 goto retry; 114 goto retry;
104 } 115 }
116
117 return ret;
118}
119EXPORT_SYMBOL(__drm_modeset_lock_all);
120
121/**
122 * drm_modeset_lock_all - take all modeset locks
123 * @dev: drm device
124 *
125 * This function takes all modeset locks, suitable where a more fine-grained
126 * scheme isn't (yet) implemented. Locks must be dropped with
127 * drm_modeset_unlock_all.
128 */
129void drm_modeset_lock_all(struct drm_device *dev)
130{
131 WARN_ON(__drm_modeset_lock_all(dev, false) != 0);
105} 132}
106EXPORT_SYMBOL(drm_modeset_lock_all); 133EXPORT_SYMBOL(drm_modeset_lock_all);
107 134
@@ -287,7 +314,12 @@ static inline int modeset_lock(struct drm_modeset_lock *lock,
287 314
288 WARN_ON(ctx->contended); 315 WARN_ON(ctx->contended);
289 316
290 if (interruptible && slow) { 317 if (ctx->trylock_only) {
318 if (!ww_mutex_trylock(&lock->mutex))
319 return -EBUSY;
320 else
321 return 0;
322 } else if (interruptible && slow) {
291 ret = ww_mutex_lock_slow_interruptible(&lock->mutex, &ctx->ww_ctx); 323 ret = ww_mutex_lock_slow_interruptible(&lock->mutex, &ctx->ww_ctx);
292 } else if (interruptible) { 324 } else if (interruptible) {
293 ret = ww_mutex_lock_interruptible(&lock->mutex, &ctx->ww_ctx); 325 ret = ww_mutex_lock_interruptible(&lock->mutex, &ctx->ww_ctx);