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