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