diff options
Diffstat (limited to 'drivers/gpu/drm/drm_modeset_lock.c')
-rw-r--r-- | drivers/gpu/drm/drm_modeset_lock.c | 213 |
1 files changed, 212 insertions, 1 deletions
diff --git a/drivers/gpu/drm/drm_modeset_lock.c b/drivers/gpu/drm/drm_modeset_lock.c index 3a02e5e3e9f3..474e4d12a2d8 100644 --- a/drivers/gpu/drm/drm_modeset_lock.c +++ b/drivers/gpu/drm/drm_modeset_lock.c | |||
@@ -57,6 +57,212 @@ | |||
57 | 57 | ||
58 | 58 | ||
59 | /** | 59 | /** |
60 | * __drm_modeset_lock_all - internal helper to grab all modeset locks | ||
61 | * @dev: DRM device | ||
62 | * @trylock: trylock mode for atomic contexts | ||
63 | * | ||
64 | * This is a special version of drm_modeset_lock_all() which can also be used in | ||
65 | * atomic contexts. Then @trylock must be set to true. | ||
66 | * | ||
67 | * Returns: | ||
68 | * 0 on success or negative error code on failure. | ||
69 | */ | ||
70 | int __drm_modeset_lock_all(struct drm_device *dev, | ||
71 | bool trylock) | ||
72 | { | ||
73 | struct drm_mode_config *config = &dev->mode_config; | ||
74 | struct drm_modeset_acquire_ctx *ctx; | ||
75 | int ret; | ||
76 | |||
77 | ctx = kzalloc(sizeof(*ctx), | ||
78 | trylock ? GFP_ATOMIC : GFP_KERNEL); | ||
79 | if (!ctx) | ||
80 | return -ENOMEM; | ||
81 | |||
82 | if (trylock) { | ||
83 | if (!mutex_trylock(&config->mutex)) | ||
84 | return -EBUSY; | ||
85 | } else { | ||
86 | mutex_lock(&config->mutex); | ||
87 | } | ||
88 | |||
89 | drm_modeset_acquire_init(ctx, 0); | ||
90 | ctx->trylock_only = trylock; | ||
91 | |||
92 | retry: | ||
93 | ret = drm_modeset_lock(&config->connection_mutex, ctx); | ||
94 | if (ret) | ||
95 | goto fail; | ||
96 | ret = drm_modeset_lock_all_crtcs(dev, ctx); | ||
97 | if (ret) | ||
98 | goto fail; | ||
99 | |||
100 | WARN_ON(config->acquire_ctx); | ||
101 | |||
102 | /* now we hold the locks, so now that it is safe, stash the | ||
103 | * ctx for drm_modeset_unlock_all(): | ||
104 | */ | ||
105 | config->acquire_ctx = ctx; | ||
106 | |||
107 | drm_warn_on_modeset_not_all_locked(dev); | ||
108 | |||
109 | return 0; | ||
110 | |||
111 | fail: | ||
112 | if (ret == -EDEADLK) { | ||
113 | drm_modeset_backoff(ctx); | ||
114 | goto retry; | ||
115 | } | ||
116 | |||
117 | return ret; | ||
118 | } | ||
119 | EXPORT_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 | */ | ||
129 | void drm_modeset_lock_all(struct drm_device *dev) | ||
130 | { | ||
131 | WARN_ON(__drm_modeset_lock_all(dev, false) != 0); | ||
132 | } | ||
133 | EXPORT_SYMBOL(drm_modeset_lock_all); | ||
134 | |||
135 | /** | ||
136 | * drm_modeset_unlock_all - drop all modeset locks | ||
137 | * @dev: device | ||
138 | * | ||
139 | * This function drop all modeset locks taken by drm_modeset_lock_all. | ||
140 | */ | ||
141 | void drm_modeset_unlock_all(struct drm_device *dev) | ||
142 | { | ||
143 | struct drm_mode_config *config = &dev->mode_config; | ||
144 | struct drm_modeset_acquire_ctx *ctx = config->acquire_ctx; | ||
145 | |||
146 | if (WARN_ON(!ctx)) | ||
147 | return; | ||
148 | |||
149 | config->acquire_ctx = NULL; | ||
150 | drm_modeset_drop_locks(ctx); | ||
151 | drm_modeset_acquire_fini(ctx); | ||
152 | |||
153 | kfree(ctx); | ||
154 | |||
155 | mutex_unlock(&dev->mode_config.mutex); | ||
156 | } | ||
157 | EXPORT_SYMBOL(drm_modeset_unlock_all); | ||
158 | |||
159 | /** | ||
160 | * drm_modeset_lock_crtc - lock crtc with hidden acquire ctx | ||
161 | * @crtc: drm crtc | ||
162 | * | ||
163 | * This function locks the given crtc using a hidden acquire context. This is | ||
164 | * necessary so that drivers internally using the atomic interfaces can grab | ||
165 | * further locks with the lock acquire context. | ||
166 | */ | ||
167 | void drm_modeset_lock_crtc(struct drm_crtc *crtc) | ||
168 | { | ||
169 | struct drm_modeset_acquire_ctx *ctx; | ||
170 | int ret; | ||
171 | |||
172 | ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); | ||
173 | if (WARN_ON(!ctx)) | ||
174 | return; | ||
175 | |||
176 | drm_modeset_acquire_init(ctx, 0); | ||
177 | |||
178 | retry: | ||
179 | ret = drm_modeset_lock(&crtc->mutex, ctx); | ||
180 | if (ret) | ||
181 | goto fail; | ||
182 | |||
183 | WARN_ON(crtc->acquire_ctx); | ||
184 | |||
185 | /* now we hold the locks, so now that it is safe, stash the | ||
186 | * ctx for drm_modeset_unlock_crtc(): | ||
187 | */ | ||
188 | crtc->acquire_ctx = ctx; | ||
189 | |||
190 | return; | ||
191 | |||
192 | fail: | ||
193 | if (ret == -EDEADLK) { | ||
194 | drm_modeset_backoff(ctx); | ||
195 | goto retry; | ||
196 | } | ||
197 | } | ||
198 | EXPORT_SYMBOL(drm_modeset_lock_crtc); | ||
199 | |||
200 | /** | ||
201 | * drm_modeset_legacy_acquire_ctx - find acquire ctx for legacy ioctls | ||
202 | * @crtc: drm crtc | ||
203 | * | ||
204 | * Legacy ioctl operations like cursor updates or page flips only have per-crtc | ||
205 | * locking, and store the acquire ctx in the corresponding crtc. All other | ||
206 | * legacy operations take all locks and use a global acquire context. This | ||
207 | * function grabs the right one. | ||
208 | */ | ||
209 | struct drm_modeset_acquire_ctx * | ||
210 | drm_modeset_legacy_acquire_ctx(struct drm_crtc *crtc) | ||
211 | { | ||
212 | if (crtc->acquire_ctx) | ||
213 | return crtc->acquire_ctx; | ||
214 | |||
215 | WARN_ON(!crtc->dev->mode_config.acquire_ctx); | ||
216 | |||
217 | return crtc->dev->mode_config.acquire_ctx; | ||
218 | } | ||
219 | EXPORT_SYMBOL(drm_modeset_legacy_acquire_ctx); | ||
220 | |||
221 | /** | ||
222 | * drm_modeset_unlock_crtc - drop crtc lock | ||
223 | * @crtc: drm crtc | ||
224 | * | ||
225 | * This drops the crtc lock acquire with drm_modeset_lock_crtc() and all other | ||
226 | * locks acquired through the hidden context. | ||
227 | */ | ||
228 | void drm_modeset_unlock_crtc(struct drm_crtc *crtc) | ||
229 | { | ||
230 | struct drm_modeset_acquire_ctx *ctx = crtc->acquire_ctx; | ||
231 | |||
232 | if (WARN_ON(!ctx)) | ||
233 | return; | ||
234 | |||
235 | crtc->acquire_ctx = NULL; | ||
236 | drm_modeset_drop_locks(ctx); | ||
237 | drm_modeset_acquire_fini(ctx); | ||
238 | |||
239 | kfree(ctx); | ||
240 | } | ||
241 | EXPORT_SYMBOL(drm_modeset_unlock_crtc); | ||
242 | |||
243 | /** | ||
244 | * drm_warn_on_modeset_not_all_locked - check that all modeset locks are locked | ||
245 | * @dev: device | ||
246 | * | ||
247 | * Useful as a debug assert. | ||
248 | */ | ||
249 | void drm_warn_on_modeset_not_all_locked(struct drm_device *dev) | ||
250 | { | ||
251 | struct drm_crtc *crtc; | ||
252 | |||
253 | /* Locking is currently fubar in the panic handler. */ | ||
254 | if (oops_in_progress) | ||
255 | return; | ||
256 | |||
257 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) | ||
258 | WARN_ON(!drm_modeset_is_locked(&crtc->mutex)); | ||
259 | |||
260 | WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex)); | ||
261 | WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); | ||
262 | } | ||
263 | EXPORT_SYMBOL(drm_warn_on_modeset_not_all_locked); | ||
264 | |||
265 | /** | ||
60 | * drm_modeset_acquire_init - initialize acquire context | 266 | * drm_modeset_acquire_init - initialize acquire context |
61 | * @ctx: the acquire context | 267 | * @ctx: the acquire context |
62 | * @flags: for future | 268 | * @flags: for future |
@@ -108,7 +314,12 @@ static inline int modeset_lock(struct drm_modeset_lock *lock, | |||
108 | 314 | ||
109 | WARN_ON(ctx->contended); | 315 | WARN_ON(ctx->contended); |
110 | 316 | ||
111 | 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) { | ||
112 | ret = ww_mutex_lock_slow_interruptible(&lock->mutex, &ctx->ww_ctx); | 323 | ret = ww_mutex_lock_slow_interruptible(&lock->mutex, &ctx->ww_ctx); |
113 | } else if (interruptible) { | 324 | } else if (interruptible) { |
114 | ret = ww_mutex_lock_interruptible(&lock->mutex, &ctx->ww_ctx); | 325 | ret = ww_mutex_lock_interruptible(&lock->mutex, &ctx->ww_ctx); |