Subversion Repositories Kolibri OS

Rev

Rev 5060 | Rev 6084 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
5060 serge 1
/*
2
 * Copyright (C) 2014 Red Hat
3
 * Author: Rob Clark 
4
 *
5
 * Permission is hereby granted, free of charge, to any person obtaining a
6
 * copy of this software and associated documentation files (the "Software"),
7
 * to deal in the Software without restriction, including without limitation
8
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9
 * and/or sell copies of the Software, and to permit persons to whom the
10
 * Software is furnished to do so, subject to the following conditions:
11
 *
12
 * The above copyright notice and this permission notice shall be included in
13
 * all copies or substantial portions of the Software.
14
 *
15
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18
 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
19
 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
20
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
21
 * OTHER DEALINGS IN THE SOFTWARE.
22
 */
23
 
24
#include 
25
#include 
26
#include 
27
 
28
/**
29
 * DOC: kms locking
30
 *
31
 * As KMS moves toward more fine grained locking, and atomic ioctl where
32
 * userspace can indirectly control locking order, it becomes necessary
33
 * to use ww_mutex and acquire-contexts to avoid deadlocks.  But because
34
 * the locking is more distributed around the driver code, we want a bit
35
 * of extra utility/tracking out of our acquire-ctx.  This is provided
36
 * by drm_modeset_lock / drm_modeset_acquire_ctx.
37
 *
5271 serge 38
 * For basic principles of ww_mutex, see: Documentation/locking/ww-mutex-design.txt
5060 serge 39
 *
40
 * The basic usage pattern is to:
41
 *
42
 *     drm_modeset_acquire_init(&ctx)
43
 *   retry:
44
 *     foreach (lock in random_ordered_set_of_locks) {
45
 *       ret = drm_modeset_lock(lock, &ctx)
46
 *       if (ret == -EDEADLK) {
47
 *          drm_modeset_backoff(&ctx);
48
 *          goto retry;
49
 *       }
50
 *     }
51
 *
52
 *     ... do stuff ...
53
 *
54
 *     drm_modeset_drop_locks(&ctx);
55
 *     drm_modeset_acquire_fini(&ctx);
56
 */
57
 
58
 
59
/**
5271 serge 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 for a plane update
161
 * @crtc: DRM CRTC
162
 * @plane: DRM plane to be updated on @crtc
163
 *
164
 * This function locks the given crtc and plane (which should be either the
165
 * primary or cursor plane) using a hidden acquire context. This is necessary so
166
 * that drivers internally using the atomic interfaces can grab further locks
167
 * with the lock acquire context.
168
 *
169
 * Note that @plane can be NULL, e.g. when the cursor support hasn't yet been
170
 * converted to universal planes yet.
171
 */
172
void drm_modeset_lock_crtc(struct drm_crtc *crtc,
173
			   struct drm_plane *plane)
174
{
175
	struct drm_modeset_acquire_ctx *ctx;
176
	int ret;
177
 
178
	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
179
	if (WARN_ON(!ctx))
180
		return;
181
 
182
	drm_modeset_acquire_init(ctx, 0);
183
 
184
retry:
185
	ret = drm_modeset_lock(&crtc->mutex, ctx);
186
	if (ret)
187
		goto fail;
188
 
189
	if (plane) {
190
		ret = drm_modeset_lock(&plane->mutex, ctx);
191
		if (ret)
192
			goto fail;
193
 
194
		if (plane->crtc) {
195
			ret = drm_modeset_lock(&plane->crtc->mutex, ctx);
196
			if (ret)
197
				goto fail;
198
		}
199
	}
200
 
201
	WARN_ON(crtc->acquire_ctx);
202
 
203
	/* now we hold the locks, so now that it is safe, stash the
204
	 * ctx for drm_modeset_unlock_crtc():
205
	 */
206
	crtc->acquire_ctx = ctx;
207
 
208
	return;
209
 
210
fail:
211
	if (ret == -EDEADLK) {
212
		drm_modeset_backoff(ctx);
213
		goto retry;
214
	}
215
}
216
EXPORT_SYMBOL(drm_modeset_lock_crtc);
217
 
218
/**
219
 * drm_modeset_legacy_acquire_ctx - find acquire ctx for legacy ioctls
220
 * @crtc: drm crtc
221
 *
222
 * Legacy ioctl operations like cursor updates or page flips only have per-crtc
223
 * locking, and store the acquire ctx in the corresponding crtc. All other
224
 * legacy operations take all locks and use a global acquire context. This
225
 * function grabs the right one.
226
 */
227
struct drm_modeset_acquire_ctx *
228
drm_modeset_legacy_acquire_ctx(struct drm_crtc *crtc)
229
{
230
	if (crtc->acquire_ctx)
231
		return crtc->acquire_ctx;
232
 
233
	WARN_ON(!crtc->dev->mode_config.acquire_ctx);
234
 
235
	return crtc->dev->mode_config.acquire_ctx;
236
}
237
EXPORT_SYMBOL(drm_modeset_legacy_acquire_ctx);
238
 
239
/**
240
 * drm_modeset_unlock_crtc - drop crtc lock
241
 * @crtc: drm crtc
242
 *
243
 * This drops the crtc lock acquire with drm_modeset_lock_crtc() and all other
244
 * locks acquired through the hidden context.
245
 */
246
void drm_modeset_unlock_crtc(struct drm_crtc *crtc)
247
{
248
	struct drm_modeset_acquire_ctx *ctx = crtc->acquire_ctx;
249
 
250
	if (WARN_ON(!ctx))
251
		return;
252
 
253
	crtc->acquire_ctx = NULL;
254
	drm_modeset_drop_locks(ctx);
255
	drm_modeset_acquire_fini(ctx);
256
 
257
	kfree(ctx);
258
}
259
EXPORT_SYMBOL(drm_modeset_unlock_crtc);
260
 
261
/**
262
 * drm_warn_on_modeset_not_all_locked - check that all modeset locks are locked
263
 * @dev: device
264
 *
265
 * Useful as a debug assert.
266
 */
267
void drm_warn_on_modeset_not_all_locked(struct drm_device *dev)
268
{
269
	struct drm_crtc *crtc;
270
 
271
	/* Locking is currently fubar in the panic handler. */
272
//   if (oops_in_progress)
273
//       return;
274
 
275
	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
276
		WARN_ON(!drm_modeset_is_locked(&crtc->mutex));
277
 
278
	WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
279
	WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
280
}
281
EXPORT_SYMBOL(drm_warn_on_modeset_not_all_locked);
282
 
283
/**
5060 serge 284
 * drm_modeset_acquire_init - initialize acquire context
285
 * @ctx: the acquire context
286
 * @flags: for future
287
 */
288
void drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx,
289
		uint32_t flags)
290
{
291
	memset(ctx, 0, sizeof(*ctx));
292
	ww_acquire_init(&ctx->ww_ctx, &crtc_ww_class);
293
	INIT_LIST_HEAD(&ctx->locked);
294
}
295
EXPORT_SYMBOL(drm_modeset_acquire_init);
296
 
297
/**
298
 * drm_modeset_acquire_fini - cleanup acquire context
299
 * @ctx: the acquire context
300
 */
301
void drm_modeset_acquire_fini(struct drm_modeset_acquire_ctx *ctx)
302
{
303
	ww_acquire_fini(&ctx->ww_ctx);
304
}
305
EXPORT_SYMBOL(drm_modeset_acquire_fini);
306
 
307
/**
308
 * drm_modeset_drop_locks - drop all locks
309
 * @ctx: the acquire context
310
 *
311
 * Drop all locks currently held against this acquire context.
312
 */
313
void drm_modeset_drop_locks(struct drm_modeset_acquire_ctx *ctx)
314
{
315
	WARN_ON(ctx->contended);
316
	while (!list_empty(&ctx->locked)) {
317
		struct drm_modeset_lock *lock;
318
 
319
		lock = list_first_entry(&ctx->locked,
320
				struct drm_modeset_lock, head);
321
 
322
		drm_modeset_unlock(lock);
323
	}
324
}
325
EXPORT_SYMBOL(drm_modeset_drop_locks);
326
 
327
static inline int modeset_lock(struct drm_modeset_lock *lock,
328
		struct drm_modeset_acquire_ctx *ctx,
329
		bool interruptible, bool slow)
330
{
331
	int ret;
332
 
333
	WARN_ON(ctx->contended);
334
 
5271 serge 335
	if (ctx->trylock_only) {
336
		if (!ww_mutex_trylock(&lock->mutex))
337
			return -EBUSY;
338
		else
339
			return 0;
340
	} else if (interruptible && slow) {
5060 serge 341
		ret = ww_mutex_lock_slow_interruptible(&lock->mutex, &ctx->ww_ctx);
342
	} else if (interruptible) {
343
		ret = ww_mutex_lock_interruptible(&lock->mutex, &ctx->ww_ctx);
344
	} else if (slow) {
345
		ww_mutex_lock_slow(&lock->mutex, &ctx->ww_ctx);
346
		ret = 0;
347
	} else {
348
		ret = ww_mutex_lock(&lock->mutex, &ctx->ww_ctx);
349
	}
350
	if (!ret) {
351
		WARN_ON(!list_empty(&lock->head));
352
		list_add(&lock->head, &ctx->locked);
353
	} else if (ret == -EALREADY) {
354
		/* we already hold the lock.. this is fine.  For atomic
355
		 * we will need to be able to drm_modeset_lock() things
356
		 * without having to keep track of what is already locked
357
		 * or not.
358
		 */
359
		ret = 0;
360
	} else if (ret == -EDEADLK) {
361
		ctx->contended = lock;
362
	}
363
 
364
	return ret;
365
}
366
 
367
static int modeset_backoff(struct drm_modeset_acquire_ctx *ctx,
368
		bool interruptible)
369
{
370
	struct drm_modeset_lock *contended = ctx->contended;
371
 
372
	ctx->contended = NULL;
373
 
374
	if (WARN_ON(!contended))
375
		return 0;
376
 
377
	drm_modeset_drop_locks(ctx);
378
 
379
	return modeset_lock(contended, ctx, interruptible, true);
380
}
381
 
382
/**
383
 * drm_modeset_backoff - deadlock avoidance backoff
384
 * @ctx: the acquire context
385
 *
386
 * If deadlock is detected (ie. drm_modeset_lock() returns -EDEADLK),
387
 * you must call this function to drop all currently held locks and
388
 * block until the contended lock becomes available.
389
 */
390
void drm_modeset_backoff(struct drm_modeset_acquire_ctx *ctx)
391
{
392
	modeset_backoff(ctx, false);
393
}
394
EXPORT_SYMBOL(drm_modeset_backoff);
395
 
396
/**
397
 * drm_modeset_backoff_interruptible - deadlock avoidance backoff
398
 * @ctx: the acquire context
399
 *
400
 * Interruptible version of drm_modeset_backoff()
401
 */
402
int drm_modeset_backoff_interruptible(struct drm_modeset_acquire_ctx *ctx)
403
{
404
	return modeset_backoff(ctx, true);
405
}
406
EXPORT_SYMBOL(drm_modeset_backoff_interruptible);
407
 
408
/**
409
 * drm_modeset_lock - take modeset lock
410
 * @lock: lock to take
411
 * @ctx: acquire ctx
412
 *
413
 * If ctx is not NULL, then its ww acquire context is used and the
414
 * lock will be tracked by the context and can be released by calling
415
 * drm_modeset_drop_locks().  If -EDEADLK is returned, this means a
416
 * deadlock scenario has been detected and it is an error to attempt
417
 * to take any more locks without first calling drm_modeset_backoff().
418
 */
419
int drm_modeset_lock(struct drm_modeset_lock *lock,
420
		struct drm_modeset_acquire_ctx *ctx)
421
{
422
	if (ctx)
423
		return modeset_lock(lock, ctx, false, false);
424
 
425
	ww_mutex_lock(&lock->mutex, NULL);
426
	return 0;
427
}
428
EXPORT_SYMBOL(drm_modeset_lock);
429
 
430
/**
431
 * drm_modeset_lock_interruptible - take modeset lock
432
 * @lock: lock to take
433
 * @ctx: acquire ctx
434
 *
435
 * Interruptible version of drm_modeset_lock()
436
 */
437
int drm_modeset_lock_interruptible(struct drm_modeset_lock *lock,
438
		struct drm_modeset_acquire_ctx *ctx)
439
{
440
	if (ctx)
441
		return modeset_lock(lock, ctx, true, false);
442
 
443
	return ww_mutex_lock_interruptible(&lock->mutex, NULL);
444
}
445
EXPORT_SYMBOL(drm_modeset_lock_interruptible);
446
 
447
/**
448
 * drm_modeset_unlock - drop modeset lock
449
 * @lock: lock to release
450
 */
451
void drm_modeset_unlock(struct drm_modeset_lock *lock)
452
{
453
	list_del_init(&lock->head);
454
	ww_mutex_unlock(&lock->mutex);
455
}
456
EXPORT_SYMBOL(drm_modeset_unlock);
457
 
5271 serge 458
/* In some legacy codepaths it's convenient to just grab all the crtc and plane
459
 * related locks. */
5060 serge 460
int drm_modeset_lock_all_crtcs(struct drm_device *dev,
461
		struct drm_modeset_acquire_ctx *ctx)
462
{
463
	struct drm_mode_config *config = &dev->mode_config;
464
	struct drm_crtc *crtc;
5271 serge 465
	struct drm_plane *plane;
5060 serge 466
	int ret = 0;
467
 
468
	list_for_each_entry(crtc, &config->crtc_list, head) {
469
		ret = drm_modeset_lock(&crtc->mutex, ctx);
470
		if (ret)
471
			return ret;
472
	}
473
 
5271 serge 474
	list_for_each_entry(plane, &config->plane_list, head) {
475
		ret = drm_modeset_lock(&plane->mutex, ctx);
476
		if (ret)
477
			return ret;
478
	}
479
 
5060 serge 480
	return 0;
481
}
482
EXPORT_SYMBOL(drm_modeset_lock_all_crtcs);