xref: /dflybsd-src/sys/dev/drm/drm_lock.c (revision 24edb8848e2499ece59b84a04f554a7a897feeab)
17f3c3d6fSHasso Tepper /*-
27f3c3d6fSHasso Tepper  * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
37f3c3d6fSHasso Tepper  * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
47f3c3d6fSHasso Tepper  * All Rights Reserved.
57f3c3d6fSHasso Tepper  *
67f3c3d6fSHasso Tepper  * Permission is hereby granted, free of charge, to any person obtaining a
77f3c3d6fSHasso Tepper  * copy of this software and associated documentation files (the "Software"),
87f3c3d6fSHasso Tepper  * to deal in the Software without restriction, including without limitation
97f3c3d6fSHasso Tepper  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
107f3c3d6fSHasso Tepper  * and/or sell copies of the Software, and to permit persons to whom the
117f3c3d6fSHasso Tepper  * Software is furnished to do so, subject to the following conditions:
127f3c3d6fSHasso Tepper  *
137f3c3d6fSHasso Tepper  * The above copyright notice and this permission notice (including the next
147f3c3d6fSHasso Tepper  * paragraph) shall be included in all copies or substantial portions of the
157f3c3d6fSHasso Tepper  * Software.
167f3c3d6fSHasso Tepper  *
177f3c3d6fSHasso Tepper  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
187f3c3d6fSHasso Tepper  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
197f3c3d6fSHasso Tepper  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
207f3c3d6fSHasso Tepper  * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
217f3c3d6fSHasso Tepper  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
227f3c3d6fSHasso Tepper  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
237f3c3d6fSHasso Tepper  * OTHER DEALINGS IN THE SOFTWARE.
247f3c3d6fSHasso Tepper  *
257f3c3d6fSHasso Tepper  * Authors:
267f3c3d6fSHasso Tepper  *    Rickard E. (Rik) Faith <faith@valinux.com>
277f3c3d6fSHasso Tepper  *    Gareth Hughes <gareth@valinux.com>
287f3c3d6fSHasso Tepper  *
295718399fSFrançois Tigeot  * $FreeBSD: src/sys/dev/drm2/drm_lock.c,v 1.1 2012/05/22 11:07:44 kib Exp $
307f3c3d6fSHasso Tepper  */
317f3c3d6fSHasso Tepper 
327f3c3d6fSHasso Tepper /** @file drm_lock.c
337f3c3d6fSHasso Tepper  * Implementation of the ioctls and other support code for dealing with the
347f3c3d6fSHasso Tepper  * hardware lock.
357f3c3d6fSHasso Tepper  *
367f3c3d6fSHasso Tepper  * The DRM hardware lock is a shared structure between the kernel and userland.
377f3c3d6fSHasso Tepper  *
387f3c3d6fSHasso Tepper  * On uncontended access where the new context was the last context, the
397f3c3d6fSHasso Tepper  * client may take the lock without dropping down into the kernel, using atomic
407f3c3d6fSHasso Tepper  * compare-and-set.
417f3c3d6fSHasso Tepper  *
427f3c3d6fSHasso Tepper  * If the client finds during compare-and-set that it was not the last owner
437f3c3d6fSHasso Tepper  * of the lock, it calls the DRM lock ioctl, which may sleep waiting for the
447f3c3d6fSHasso Tepper  * lock, and may have side-effects of kernel-managed context switching.
457f3c3d6fSHasso Tepper  *
467f3c3d6fSHasso Tepper  * When the client releases the lock, if the lock is marked as being contended
477f3c3d6fSHasso Tepper  * by another client, then the DRM unlock ioctl is called so that the
487f3c3d6fSHasso Tepper  * contending client may be woken up.
497f3c3d6fSHasso Tepper  */
507f3c3d6fSHasso Tepper 
5118e26a6dSFrançois Tigeot #include <drm/drmP.h>
52*24edb884SFrançois Tigeot #include "drm_legacy.h"
537f3c3d6fSHasso Tepper 
54b3705d71SHasso Tepper int drm_lock(struct drm_device *dev, void *data, struct drm_file *file_priv)
557f3c3d6fSHasso Tepper {
56b3705d71SHasso Tepper 	struct drm_lock *lock = data;
57b3705d71SHasso Tepper 	int ret = 0;
58b3705d71SHasso Tepper 
59b3705d71SHasso Tepper 	if (lock->context == DRM_KERNEL_CONTEXT) {
60b3705d71SHasso Tepper 		DRM_ERROR("Process %d using kernel context %d\n",
61b3705d71SHasso Tepper 		    DRM_CURRENTPID, lock->context);
62b3705d71SHasso Tepper 		return EINVAL;
63b3705d71SHasso Tepper 	}
64b3705d71SHasso Tepper 
65b3705d71SHasso Tepper 	DRM_DEBUG("%d (pid %d) requests lock (0x%08x), flags = 0x%08x\n",
6679f713b0SFrançois Tigeot 	    lock->context, DRM_CURRENTPID, dev->lock.hw_lock->lock,
67b3705d71SHasso Tepper 	    lock->flags);
68b3705d71SHasso Tepper 
69b3705d71SHasso Tepper 	if (drm_core_check_feature(dev, DRIVER_DMA_QUEUE) &&
70b3705d71SHasso Tepper 	    lock->context < 0)
71b3705d71SHasso Tepper 		return EINVAL;
72b3705d71SHasso Tepper 
735718399fSFrançois Tigeot 	DRM_LOCK(dev);
74b3705d71SHasso Tepper 	for (;;) {
7579f713b0SFrançois Tigeot 		if (drm_lock_take(&dev->lock, lock->context)) {
7679f713b0SFrançois Tigeot 			dev->lock.file_priv = file_priv;
7779f713b0SFrançois Tigeot 			dev->lock.lock_time = jiffies;
78b3705d71SHasso Tepper 			atomic_inc(&dev->counts[_DRM_STAT_LOCKS]);
79b3705d71SHasso Tepper 			break;  /* Got lock */
80b3705d71SHasso Tepper 		}
81b3705d71SHasso Tepper 
82b3705d71SHasso Tepper 		/* Contention */
8379f713b0SFrançois Tigeot 		ret = DRM_LOCK_SLEEP(dev, &dev->lock.lock_queue,
845718399fSFrançois Tigeot 		    PCATCH, "drmlk2", 0);
85b3705d71SHasso Tepper 		if (ret != 0)
86b3705d71SHasso Tepper 			break;
87b3705d71SHasso Tepper 	}
885718399fSFrançois Tigeot 	DRM_UNLOCK(dev);
89b3705d71SHasso Tepper 
90b3705d71SHasso Tepper 	if (ret == ERESTART)
91b3705d71SHasso Tepper 		DRM_DEBUG("restarting syscall\n");
92b3705d71SHasso Tepper 	else
93b3705d71SHasso Tepper 		DRM_DEBUG("%d %s\n", lock->context,
94b3705d71SHasso Tepper 		    ret ? "interrupted" : "has lock");
95b3705d71SHasso Tepper 
96b3705d71SHasso Tepper 	if (ret != 0)
97b3705d71SHasso Tepper 		return ret;
98b3705d71SHasso Tepper 
99b3705d71SHasso Tepper 	/* XXX: Add signal blocking here */
100b3705d71SHasso Tepper 
101b3705d71SHasso Tepper 	if (dev->driver->dma_quiescent != NULL &&
102b3705d71SHasso Tepper 	    (lock->flags & _DRM_LOCK_QUIESCENT))
103b3705d71SHasso Tepper 		dev->driver->dma_quiescent(dev);
104b3705d71SHasso Tepper 
105b3705d71SHasso Tepper 	return 0;
106b3705d71SHasso Tepper }
107b3705d71SHasso Tepper 
108b3705d71SHasso Tepper int drm_unlock(struct drm_device *dev, void *data, struct drm_file *file_priv)
109b3705d71SHasso Tepper {
110b3705d71SHasso Tepper 	struct drm_lock *lock = data;
111b3705d71SHasso Tepper 
112b3705d71SHasso Tepper 	DRM_DEBUG("%d (pid %d) requests unlock (0x%08x), flags = 0x%08x\n",
11379f713b0SFrançois Tigeot 	    lock->context, DRM_CURRENTPID, dev->lock.hw_lock->lock,
114b3705d71SHasso Tepper 	    lock->flags);
115b3705d71SHasso Tepper 
116b3705d71SHasso Tepper 	if (lock->context == DRM_KERNEL_CONTEXT) {
117b3705d71SHasso Tepper 		DRM_ERROR("Process %d using kernel context %d\n",
118b3705d71SHasso Tepper 		    DRM_CURRENTPID, lock->context);
119b3705d71SHasso Tepper 		return EINVAL;
120b3705d71SHasso Tepper 	}
121b3705d71SHasso Tepper 
122b3705d71SHasso Tepper 	atomic_inc(&dev->counts[_DRM_STAT_UNLOCKS]);
123b3705d71SHasso Tepper 
1245718399fSFrançois Tigeot 	DRM_LOCK(dev);
12579f713b0SFrançois Tigeot 	drm_lock_transfer(&dev->lock, DRM_KERNEL_CONTEXT);
126b3705d71SHasso Tepper 
12779f713b0SFrançois Tigeot 	if (drm_lock_free(&dev->lock, DRM_KERNEL_CONTEXT)) {
128b3705d71SHasso Tepper 		DRM_ERROR("\n");
129b3705d71SHasso Tepper 	}
1305718399fSFrançois Tigeot 	DRM_UNLOCK(dev);
131b3705d71SHasso Tepper 
132b3705d71SHasso Tepper 	return 0;
133b3705d71SHasso Tepper }
134b3705d71SHasso Tepper 
135b3705d71SHasso Tepper int drm_lock_take(struct drm_lock_data *lock_data, unsigned int context)
136b3705d71SHasso Tepper {
137b3705d71SHasso Tepper 	volatile unsigned int *lock = &lock_data->hw_lock->lock;
1387f3c3d6fSHasso Tepper 	unsigned int old, new;
1397f3c3d6fSHasso Tepper 
1407f3c3d6fSHasso Tepper 	do {
1417f3c3d6fSHasso Tepper 		old = *lock;
142b3705d71SHasso Tepper 		if (old & _DRM_LOCK_HELD)
143b3705d71SHasso Tepper 			new = old | _DRM_LOCK_CONT;
144b3705d71SHasso Tepper 		else
145b3705d71SHasso Tepper 			new = context | _DRM_LOCK_HELD;
1467f3c3d6fSHasso Tepper 	} while (!atomic_cmpset_int(lock, old, new));
1477f3c3d6fSHasso Tepper 
1487f3c3d6fSHasso Tepper 	if (_DRM_LOCKING_CONTEXT(old) == context) {
1497f3c3d6fSHasso Tepper 		if (old & _DRM_LOCK_HELD) {
1507f3c3d6fSHasso Tepper 			if (context != DRM_KERNEL_CONTEXT) {
1517f3c3d6fSHasso Tepper 				DRM_ERROR("%d holds heavyweight lock\n",
1527f3c3d6fSHasso Tepper 				    context);
1537f3c3d6fSHasso Tepper 			}
1547f3c3d6fSHasso Tepper 			return 0;
1557f3c3d6fSHasso Tepper 		}
1567f3c3d6fSHasso Tepper 	}
1577f3c3d6fSHasso Tepper 	if (new == (context | _DRM_LOCK_HELD)) {
1587f3c3d6fSHasso Tepper 		/* Have lock */
1597f3c3d6fSHasso Tepper 		return 1;
1607f3c3d6fSHasso Tepper 	}
1617f3c3d6fSHasso Tepper 	return 0;
1627f3c3d6fSHasso Tepper }
1637f3c3d6fSHasso Tepper 
1647f3c3d6fSHasso Tepper /* This takes a lock forcibly and hands it to context.	Should ONLY be used
1657f3c3d6fSHasso Tepper    inside *_unlock to give lock to kernel before calling *_dma_schedule. */
166b3705d71SHasso Tepper int drm_lock_transfer(struct drm_lock_data *lock_data, unsigned int context)
1677f3c3d6fSHasso Tepper {
168b3705d71SHasso Tepper 	volatile unsigned int *lock = &lock_data->hw_lock->lock;
1697f3c3d6fSHasso Tepper 	unsigned int old, new;
1707f3c3d6fSHasso Tepper 
171b3705d71SHasso Tepper 	lock_data->file_priv = NULL;
1727f3c3d6fSHasso Tepper 	do {
1737f3c3d6fSHasso Tepper 		old = *lock;
1747f3c3d6fSHasso Tepper 		new = context | _DRM_LOCK_HELD;
1757f3c3d6fSHasso Tepper 	} while (!atomic_cmpset_int(lock, old, new));
1767f3c3d6fSHasso Tepper 
1777f3c3d6fSHasso Tepper 	return 1;
1787f3c3d6fSHasso Tepper }
1797f3c3d6fSHasso Tepper 
180b3705d71SHasso Tepper int drm_lock_free(struct drm_lock_data *lock_data, unsigned int context)
1817f3c3d6fSHasso Tepper {
182b3705d71SHasso Tepper 	volatile unsigned int *lock = &lock_data->hw_lock->lock;
1837f3c3d6fSHasso Tepper 	unsigned int old, new;
1847f3c3d6fSHasso Tepper 
185b3705d71SHasso Tepper 	lock_data->file_priv = NULL;
1867f3c3d6fSHasso Tepper 	do {
1877f3c3d6fSHasso Tepper 		old = *lock;
1887f3c3d6fSHasso Tepper 		new = 0;
1897f3c3d6fSHasso Tepper 	} while (!atomic_cmpset_int(lock, old, new));
1907f3c3d6fSHasso Tepper 
1917f3c3d6fSHasso Tepper 	if (_DRM_LOCK_IS_HELD(old) && _DRM_LOCKING_CONTEXT(old) != context) {
1927f3c3d6fSHasso Tepper 		DRM_ERROR("%d freed heavyweight lock held by %d\n",
1937f3c3d6fSHasso Tepper 		    context, _DRM_LOCKING_CONTEXT(old));
1947f3c3d6fSHasso Tepper 		return 1;
1957f3c3d6fSHasso Tepper 	}
196c6f73aabSFrançois Tigeot #if 0
197c6f73aabSFrançois Tigeot 	wake_up_interruptible(&lock_data->lock_queue);
198c6f73aabSFrançois Tigeot #endif
1997f3c3d6fSHasso Tepper 	return 0;
2007f3c3d6fSHasso Tepper }
201