1145132Sanholt/*- 2145132Sanholt * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. 3145132Sanholt * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. 4145132Sanholt * All Rights Reserved. 5145132Sanholt * 6145132Sanholt * Permission is hereby granted, free of charge, to any person obtaining a 7145132Sanholt * copy of this software and associated documentation files (the "Software"), 8145132Sanholt * to deal in the Software without restriction, including without limitation 9145132Sanholt * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10145132Sanholt * and/or sell copies of the Software, and to permit persons to whom the 11145132Sanholt * Software is furnished to do so, subject to the following conditions: 12145132Sanholt * 13145132Sanholt * The above copyright notice and this permission notice (including the next 14145132Sanholt * paragraph) shall be included in all copies or substantial portions of the 15145132Sanholt * Software. 16145132Sanholt * 17145132Sanholt * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18145132Sanholt * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19145132Sanholt * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20145132Sanholt * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 21145132Sanholt * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 22145132Sanholt * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 23145132Sanholt * OTHER DEALINGS IN THE SOFTWARE. 24145132Sanholt * 25145132Sanholt * Authors: 26145132Sanholt * Rickard E. (Rik) Faith <faith@valinux.com> 27145132Sanholt * Gareth Hughes <gareth@valinux.com> 28145132Sanholt * 29145132Sanholt */ 30145132Sanholt 31152909Sanholt#include <sys/cdefs.h> 32152909Sanholt__FBSDID("$FreeBSD$"); 33152909Sanholt 34182080Srnoland/** @file drm_lock.c 35182080Srnoland * Implementation of the ioctls and other support code for dealing with the 36182080Srnoland * hardware lock. 37182080Srnoland * 38182080Srnoland * The DRM hardware lock is a shared structure between the kernel and userland. 39182080Srnoland * 40182080Srnoland * On uncontended access where the new context was the last context, the 41182080Srnoland * client may take the lock without dropping down into the kernel, using atomic 42182080Srnoland * compare-and-set. 43182080Srnoland * 44182080Srnoland * If the client finds during compare-and-set that it was not the last owner 45182080Srnoland * of the lock, it calls the DRM lock ioctl, which may sleep waiting for the 46182080Srnoland * lock, and may have side-effects of kernel-managed context switching. 47182080Srnoland * 48182080Srnoland * When the client releases the lock, if the lock is marked as being contended 49182080Srnoland * by another client, then the DRM unlock ioctl is called so that the 50182080Srnoland * contending client may be woken up. 51182080Srnoland */ 52182080Srnoland 53145132Sanholt#include "dev/drm/drmP.h" 54145132Sanholt 55182080Srnolandint drm_lock(struct drm_device *dev, void *data, struct drm_file *file_priv) 56145132Sanholt{ 57183573Srnoland struct drm_lock *lock = data; 58183573Srnoland int ret = 0; 59145132Sanholt 60183573Srnoland if (lock->context == DRM_KERNEL_CONTEXT) { 61183573Srnoland DRM_ERROR("Process %d using kernel context %d\n", 62182080Srnoland DRM_CURRENTPID, lock->context); 63183573Srnoland return EINVAL; 64183573Srnoland } 65145132Sanholt 66183573Srnoland DRM_DEBUG("%d (pid %d) requests lock (0x%08x), flags = 0x%08x\n", 67182080Srnoland lock->context, DRM_CURRENTPID, dev->lock.hw_lock->lock, 68182080Srnoland lock->flags); 69145132Sanholt 70183573Srnoland if (drm_core_check_feature(dev, DRIVER_DMA_QUEUE) && 71183573Srnoland lock->context < 0) 72183573Srnoland return EINVAL; 73145132Sanholt 74145132Sanholt DRM_LOCK(); 75145132Sanholt for (;;) { 76183573Srnoland if (drm_lock_take(&dev->lock, lock->context)) { 77182080Srnoland dev->lock.file_priv = file_priv; 78145132Sanholt dev->lock.lock_time = jiffies; 79145132Sanholt atomic_inc(&dev->counts[_DRM_STAT_LOCKS]); 80145132Sanholt break; /* Got lock */ 81145132Sanholt } 82145132Sanholt 83145132Sanholt /* Contention */ 84182080Srnoland ret = mtx_sleep((void *)&dev->lock.lock_queue, &dev->dev_lock, 85189869Srnoland PCATCH, "drmlk2", 0); 86145132Sanholt if (ret != 0) 87145132Sanholt break; 88145132Sanholt } 89145132Sanholt DRM_UNLOCK(); 90145132Sanholt 91190023Srnoland if (ret == ERESTART) 92190023Srnoland DRM_DEBUG("restarting syscall\n"); 93190023Srnoland else 94190023Srnoland DRM_DEBUG("%d %s\n", lock->context, 95190023Srnoland ret ? "interrupted" : "has lock"); 96190023Srnoland 97145132Sanholt if (ret != 0) 98145132Sanholt return ret; 99145132Sanholt 100145132Sanholt /* XXX: Add signal blocking here */ 101145132Sanholt 102183573Srnoland if (dev->driver->dma_quiescent != NULL && 103182080Srnoland (lock->flags & _DRM_LOCK_QUIESCENT)) 104183573Srnoland dev->driver->dma_quiescent(dev); 105145132Sanholt 106145132Sanholt return 0; 107145132Sanholt} 108145132Sanholt 109182080Srnolandint drm_unlock(struct drm_device *dev, void *data, struct drm_file *file_priv) 110145132Sanholt{ 111183573Srnoland struct drm_lock *lock = data; 112145132Sanholt 113184212Srnoland DRM_DEBUG("%d (pid %d) requests unlock (0x%08x), flags = 0x%08x\n", 114184212Srnoland lock->context, DRM_CURRENTPID, dev->lock.hw_lock->lock, 115184212Srnoland lock->flags); 116184212Srnoland 117182080Srnoland if (lock->context == DRM_KERNEL_CONTEXT) { 118145132Sanholt DRM_ERROR("Process %d using kernel context %d\n", 119182080Srnoland DRM_CURRENTPID, lock->context); 120145132Sanholt return EINVAL; 121145132Sanholt } 122183573Srnoland 123145132Sanholt atomic_inc(&dev->counts[_DRM_STAT_UNLOCKS]); 124145132Sanholt 125145132Sanholt DRM_LOCK(); 126183573Srnoland drm_lock_transfer(&dev->lock, DRM_KERNEL_CONTEXT); 127145132Sanholt 128183573Srnoland if (drm_lock_free(&dev->lock, DRM_KERNEL_CONTEXT)) { 129145132Sanholt DRM_ERROR("\n"); 130145132Sanholt } 131145132Sanholt DRM_UNLOCK(); 132145132Sanholt 133145132Sanholt return 0; 134145132Sanholt} 135183573Srnoland 136183573Srnolandint drm_lock_take(struct drm_lock_data *lock_data, unsigned int context) 137183573Srnoland{ 138183573Srnoland volatile unsigned int *lock = &lock_data->hw_lock->lock; 139183573Srnoland unsigned int old, new; 140183573Srnoland 141183573Srnoland do { 142183573Srnoland old = *lock; 143183573Srnoland if (old & _DRM_LOCK_HELD) 144183573Srnoland new = old | _DRM_LOCK_CONT; 145183573Srnoland else 146183573Srnoland new = context | _DRM_LOCK_HELD; 147183573Srnoland } while (!atomic_cmpset_int(lock, old, new)); 148183573Srnoland 149183573Srnoland if (_DRM_LOCKING_CONTEXT(old) == context) { 150183573Srnoland if (old & _DRM_LOCK_HELD) { 151183573Srnoland if (context != DRM_KERNEL_CONTEXT) { 152183573Srnoland DRM_ERROR("%d holds heavyweight lock\n", 153183573Srnoland context); 154183573Srnoland } 155183573Srnoland return 0; 156183573Srnoland } 157183573Srnoland } 158183573Srnoland if (new == (context | _DRM_LOCK_HELD)) { 159183573Srnoland /* Have lock */ 160183573Srnoland return 1; 161183573Srnoland } 162183573Srnoland return 0; 163183573Srnoland} 164183573Srnoland 165183573Srnoland/* This takes a lock forcibly and hands it to context. Should ONLY be used 166183573Srnoland inside *_unlock to give lock to kernel before calling *_dma_schedule. */ 167183573Srnolandint drm_lock_transfer(struct drm_lock_data *lock_data, unsigned int context) 168183573Srnoland{ 169183573Srnoland volatile unsigned int *lock = &lock_data->hw_lock->lock; 170183573Srnoland unsigned int old, new; 171183573Srnoland 172183573Srnoland lock_data->file_priv = NULL; 173183573Srnoland do { 174183573Srnoland old = *lock; 175183573Srnoland new = context | _DRM_LOCK_HELD; 176183573Srnoland } while (!atomic_cmpset_int(lock, old, new)); 177183573Srnoland 178183573Srnoland return 1; 179183573Srnoland} 180183573Srnoland 181183573Srnolandint drm_lock_free(struct drm_lock_data *lock_data, unsigned int context) 182183573Srnoland{ 183183573Srnoland volatile unsigned int *lock = &lock_data->hw_lock->lock; 184183573Srnoland unsigned int old, new; 185183573Srnoland 186183573Srnoland lock_data->file_priv = NULL; 187183573Srnoland do { 188183573Srnoland old = *lock; 189183573Srnoland new = 0; 190183573Srnoland } while (!atomic_cmpset_int(lock, old, new)); 191183573Srnoland 192183573Srnoland if (_DRM_LOCK_IS_HELD(old) && _DRM_LOCKING_CONTEXT(old) != context) { 193183573Srnoland DRM_ERROR("%d freed heavyweight lock held by %d\n", 194183573Srnoland context, _DRM_LOCKING_CONTEXT(old)); 195183573Srnoland return 1; 196183573Srnoland } 197183573Srnoland DRM_WAKEUP_INT((void *)&lock_data->lock_queue); 198183573Srnoland return 0; 199183573Srnoland} 200