drm_lock.c revision 145132
1145132Sanholt/* lock.c -- IOCTLs for locking -*- linux-c -*- 2145132Sanholt * Created: Tue Feb 2 08:37:54 1999 by faith@valinux.com 3145132Sanholt */ 4145132Sanholt/*- 5145132Sanholt * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. 6145132Sanholt * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. 7145132Sanholt * All Rights Reserved. 8145132Sanholt * 9145132Sanholt * Permission is hereby granted, free of charge, to any person obtaining a 10145132Sanholt * copy of this software and associated documentation files (the "Software"), 11145132Sanholt * to deal in the Software without restriction, including without limitation 12145132Sanholt * the rights to use, copy, modify, merge, publish, distribute, sublicense, 13145132Sanholt * and/or sell copies of the Software, and to permit persons to whom the 14145132Sanholt * Software is furnished to do so, subject to the following conditions: 15145132Sanholt * 16145132Sanholt * The above copyright notice and this permission notice (including the next 17145132Sanholt * paragraph) shall be included in all copies or substantial portions of the 18145132Sanholt * Software. 19145132Sanholt * 20145132Sanholt * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21145132Sanholt * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22145132Sanholt * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 23145132Sanholt * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 24145132Sanholt * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 25145132Sanholt * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 26145132Sanholt * OTHER DEALINGS IN THE SOFTWARE. 27145132Sanholt * 28145132Sanholt * Authors: 29145132Sanholt * Rickard E. (Rik) Faith <faith@valinux.com> 30145132Sanholt * Gareth Hughes <gareth@valinux.com> 31145132Sanholt * 32145132Sanholt * $FreeBSD: head/sys/dev/drm/drm_lock.c 145132 2005-04-16 03:44:47Z anholt $ 33145132Sanholt */ 34145132Sanholt 35145132Sanholt#include "dev/drm/drmP.h" 36145132Sanholt 37145132Sanholtint drm_lock_take(__volatile__ unsigned int *lock, unsigned int context) 38145132Sanholt{ 39145132Sanholt unsigned int old, new; 40145132Sanholt 41145132Sanholt do { 42145132Sanholt old = *lock; 43145132Sanholt if (old & _DRM_LOCK_HELD) new = old | _DRM_LOCK_CONT; 44145132Sanholt else new = context | _DRM_LOCK_HELD; 45145132Sanholt } while (!atomic_cmpset_int(lock, old, new)); 46145132Sanholt 47145132Sanholt if (_DRM_LOCKING_CONTEXT(old) == context) { 48145132Sanholt if (old & _DRM_LOCK_HELD) { 49145132Sanholt if (context != DRM_KERNEL_CONTEXT) { 50145132Sanholt DRM_ERROR("%d holds heavyweight lock\n", 51145132Sanholt context); 52145132Sanholt } 53145132Sanholt return 0; 54145132Sanholt } 55145132Sanholt } 56145132Sanholt if (new == (context | _DRM_LOCK_HELD)) { 57145132Sanholt /* Have lock */ 58145132Sanholt return 1; 59145132Sanholt } 60145132Sanholt return 0; 61145132Sanholt} 62145132Sanholt 63145132Sanholt/* This takes a lock forcibly and hands it to context. Should ONLY be used 64145132Sanholt inside *_unlock to give lock to kernel before calling *_dma_schedule. */ 65145132Sanholtint drm_lock_transfer(drm_device_t *dev, 66145132Sanholt __volatile__ unsigned int *lock, unsigned int context) 67145132Sanholt{ 68145132Sanholt unsigned int old, new; 69145132Sanholt 70145132Sanholt dev->lock.filp = NULL; 71145132Sanholt do { 72145132Sanholt old = *lock; 73145132Sanholt new = context | _DRM_LOCK_HELD; 74145132Sanholt } while (!atomic_cmpset_int(lock, old, new)); 75145132Sanholt 76145132Sanholt return 1; 77145132Sanholt} 78145132Sanholt 79145132Sanholtint drm_lock_free(drm_device_t *dev, 80145132Sanholt __volatile__ unsigned int *lock, unsigned int context) 81145132Sanholt{ 82145132Sanholt unsigned int old, new; 83145132Sanholt 84145132Sanholt dev->lock.filp = NULL; 85145132Sanholt do { 86145132Sanholt old = *lock; 87145132Sanholt new = 0; 88145132Sanholt } while (!atomic_cmpset_int(lock, old, new)); 89145132Sanholt 90145132Sanholt if (_DRM_LOCK_IS_HELD(old) && _DRM_LOCKING_CONTEXT(old) != context) { 91145132Sanholt DRM_ERROR("%d freed heavyweight lock held by %d\n", 92145132Sanholt context, _DRM_LOCKING_CONTEXT(old)); 93145132Sanholt return 1; 94145132Sanholt } 95145132Sanholt DRM_WAKEUP_INT((void *)&dev->lock.lock_queue); 96145132Sanholt return 0; 97145132Sanholt} 98145132Sanholt 99145132Sanholtint drm_lock(DRM_IOCTL_ARGS) 100145132Sanholt{ 101145132Sanholt DRM_DEVICE; 102145132Sanholt drm_lock_t lock; 103145132Sanholt int ret = 0; 104145132Sanholt 105145132Sanholt DRM_COPY_FROM_USER_IOCTL(lock, (drm_lock_t *)data, sizeof(lock)); 106145132Sanholt 107145132Sanholt if (lock.context == DRM_KERNEL_CONTEXT) { 108145132Sanholt DRM_ERROR("Process %d using kernel context %d\n", 109145132Sanholt DRM_CURRENTPID, lock.context); 110145132Sanholt return EINVAL; 111145132Sanholt } 112145132Sanholt 113145132Sanholt DRM_DEBUG("%d (pid %d) requests lock (0x%08x), flags = 0x%08x\n", 114145132Sanholt lock.context, DRM_CURRENTPID, dev->lock.hw_lock->lock, lock.flags); 115145132Sanholt 116145132Sanholt if (dev->use_dma_queue && lock.context < 0) 117145132Sanholt return EINVAL; 118145132Sanholt 119145132Sanholt DRM_LOCK(); 120145132Sanholt for (;;) { 121145132Sanholt if (drm_lock_take(&dev->lock.hw_lock->lock, lock.context)) { 122145132Sanholt dev->lock.filp = (void *)(uintptr_t)DRM_CURRENTPID; 123145132Sanholt dev->lock.lock_time = jiffies; 124145132Sanholt atomic_inc(&dev->counts[_DRM_STAT_LOCKS]); 125145132Sanholt break; /* Got lock */ 126145132Sanholt } 127145132Sanholt 128145132Sanholt /* Contention */ 129145132Sanholt#if defined(__FreeBSD__) && __FreeBSD_version > 500000 130145132Sanholt ret = msleep((void *)&dev->lock.lock_queue, &dev->dev_lock, 131145132Sanholt PZERO | PCATCH, "drmlk2", 0); 132145132Sanholt#else 133145132Sanholt ret = tsleep((void *)&dev->lock.lock_queue, PZERO | PCATCH, 134145132Sanholt "drmlk2", 0); 135145132Sanholt#endif 136145132Sanholt if (ret != 0) 137145132Sanholt break; 138145132Sanholt } 139145132Sanholt DRM_UNLOCK(); 140145132Sanholt DRM_DEBUG("%d %s\n", lock.context, ret ? "interrupted" : "has lock"); 141145132Sanholt 142145132Sanholt if (ret != 0) 143145132Sanholt return ret; 144145132Sanholt 145145132Sanholt /* XXX: Add signal blocking here */ 146145132Sanholt 147145132Sanholt if (dev->dma_quiescent != NULL && (lock.flags & _DRM_LOCK_QUIESCENT)) 148145132Sanholt dev->dma_quiescent(dev); 149145132Sanholt 150145132Sanholt return 0; 151145132Sanholt} 152145132Sanholt 153145132Sanholtint drm_unlock(DRM_IOCTL_ARGS) 154145132Sanholt{ 155145132Sanholt DRM_DEVICE; 156145132Sanholt drm_lock_t lock; 157145132Sanholt 158145132Sanholt DRM_COPY_FROM_USER_IOCTL(lock, (drm_lock_t *)data, sizeof(lock)); 159145132Sanholt 160145132Sanholt if (lock.context == DRM_KERNEL_CONTEXT) { 161145132Sanholt DRM_ERROR("Process %d using kernel context %d\n", 162145132Sanholt DRM_CURRENTPID, lock.context); 163145132Sanholt return EINVAL; 164145132Sanholt } 165145132Sanholt 166145132Sanholt atomic_inc(&dev->counts[_DRM_STAT_UNLOCKS]); 167145132Sanholt 168145132Sanholt DRM_LOCK(); 169145132Sanholt drm_lock_transfer(dev, &dev->lock.hw_lock->lock, DRM_KERNEL_CONTEXT); 170145132Sanholt 171145132Sanholt if (drm_lock_free(dev, &dev->lock.hw_lock->lock, DRM_KERNEL_CONTEXT)) { 172145132Sanholt DRM_ERROR("\n"); 173145132Sanholt } 174145132Sanholt DRM_UNLOCK(); 175145132Sanholt 176145132Sanholt return 0; 177145132Sanholt} 178