1/*-
2 * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
3 * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
4 * All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice (including the next
14 * paragraph) shall be included in all copies or substantial portions of the
15 * Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
20 * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
21 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
22 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23 * OTHER DEALINGS IN THE SOFTWARE.
24 *
25 * Authors:
26 *    Rickard E. (Rik) Faith <faith@valinux.com>
27 *    Gareth Hughes <gareth@valinux.com>
28 *
29 */
30
31/** @file drm_context.c
32 * Implementation of the context management ioctls.
33 */
34
35#include "drmP.h"
36
37/* ================================================================
38 * Context bitmap support
39 */
40
41void drm_ctxbitmap_free(struct drm_device *dev, int ctx_handle)
42{
43	if (ctx_handle < 0 || ctx_handle >= DRM_MAX_CTXBITMAP ||
44	    dev->ctx_bitmap == NULL) {
45		DRM_ERROR("Attempt to free invalid context handle: %d\n",
46		   ctx_handle);
47		return;
48	}
49
50	DRM_LOCK();
51	clear_bit(ctx_handle, dev->ctx_bitmap);
52	dev->context_sareas[ctx_handle] = NULL;
53	DRM_UNLOCK();
54	return;
55}
56
57int drm_ctxbitmap_next(struct drm_device *dev)
58{
59	int bit;
60
61	if (dev->ctx_bitmap == NULL)
62		return -1;
63
64	DRM_LOCK();
65	bit = find_first_zero_bit(dev->ctx_bitmap, DRM_MAX_CTXBITMAP);
66	if (bit >= DRM_MAX_CTXBITMAP) {
67		DRM_UNLOCK();
68		return -1;
69	}
70
71	set_bit(bit, dev->ctx_bitmap);
72	DRM_DEBUG("drm_ctxbitmap_next bit : %d\n", bit);
73	if ((bit+1) > dev->max_context) {
74		dev->max_context = (bit+1);
75		if (dev->context_sareas != NULL) {
76			drm_local_map_t **ctx_sareas;
77
78			ctx_sareas = realloc(dev->context_sareas,
79			    dev->max_context * sizeof(*dev->context_sareas),
80			    DRM_MEM_SAREA, M_NOWAIT);
81			if (ctx_sareas == NULL) {
82				clear_bit(bit, dev->ctx_bitmap);
83				DRM_UNLOCK();
84				return -1;
85			}
86			dev->context_sareas = ctx_sareas;
87			dev->context_sareas[bit] = NULL;
88		} else {
89			/* max_context == 1 at this point */
90			dev->context_sareas = malloc(dev->max_context *
91			    sizeof(*dev->context_sareas), DRM_MEM_SAREA,
92			    M_NOWAIT);
93			if (dev->context_sareas == NULL) {
94				clear_bit(bit, dev->ctx_bitmap);
95				DRM_UNLOCK();
96				return -1;
97			}
98			dev->context_sareas[bit] = NULL;
99		}
100	}
101	DRM_UNLOCK();
102	return bit;
103}
104
105int drm_ctxbitmap_init(struct drm_device *dev)
106{
107	int i;
108   	int temp;
109
110	DRM_LOCK();
111	dev->ctx_bitmap = malloc(PAGE_SIZE, DRM_MEM_CTXBITMAP,
112	    M_NOWAIT | M_ZERO);
113	if (dev->ctx_bitmap == NULL) {
114		DRM_UNLOCK();
115		return ENOMEM;
116	}
117	dev->context_sareas = NULL;
118	dev->max_context = -1;
119	DRM_UNLOCK();
120
121	for (i = 0; i < DRM_RESERVED_CONTEXTS; i++) {
122		temp = drm_ctxbitmap_next(dev);
123		DRM_DEBUG("drm_ctxbitmap_init : %d\n", temp);
124	}
125
126	return 0;
127}
128
129void drm_ctxbitmap_cleanup(struct drm_device *dev)
130{
131	DRM_LOCK();
132	if (dev->context_sareas != NULL)
133		free(dev->context_sareas, DRM_MEM_SAREA);
134	free(dev->ctx_bitmap, DRM_MEM_CTXBITMAP);
135	DRM_UNLOCK();
136}
137
138/* ================================================================
139 * Per Context SAREA Support
140 */
141
142int drm_getsareactx(struct drm_device *dev, void *data,
143		    struct drm_file *file_priv)
144{
145	struct drm_ctx_priv_map *request = data;
146	drm_local_map_t *map;
147
148	DRM_LOCK();
149	if (dev->max_context < 0 ||
150	    request->ctx_id >= (unsigned) dev->max_context) {
151		DRM_UNLOCK();
152		return EINVAL;
153	}
154
155	map = dev->context_sareas[request->ctx_id];
156	DRM_UNLOCK();
157
158	request->handle = map->handle;
159
160	return 0;
161}
162
163int drm_setsareactx(struct drm_device *dev, void *data,
164		    struct drm_file *file_priv)
165{
166	struct drm_ctx_priv_map *request = data;
167	drm_local_map_t *map = NULL;
168
169	DRM_LOCK();
170	TAILQ_FOREACH(map, &dev->maplist, link) {
171		if (map->handle == request->handle) {
172			if (dev->max_context < 0)
173				goto bad;
174			if (request->ctx_id >= (unsigned) dev->max_context)
175				goto bad;
176			dev->context_sareas[request->ctx_id] = map;
177			DRM_UNLOCK();
178			return 0;
179		}
180	}
181
182bad:
183	DRM_UNLOCK();
184	return EINVAL;
185}
186
187/* ================================================================
188 * The actual DRM context handling routines
189 */
190
191int drm_context_switch(struct drm_device *dev, int old, int new)
192{
193	if (test_and_set_bit(0, &dev->context_flag)) {
194		DRM_ERROR("Reentering -- FIXME\n");
195		return EBUSY;
196	}
197
198	DRM_DEBUG("Context switch from %d to %d\n", old, new);
199
200	if (new == dev->last_context) {
201		clear_bit(0, &dev->context_flag);
202		return 0;
203	}
204
205	return 0;
206}
207
208int drm_context_switch_complete(struct drm_device *dev, int new)
209{
210	dev->last_context = new;  /* PRE/POST: This is the _only_ writer. */
211
212	if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
213		DRM_ERROR("Lock isn't held after context switch\n");
214	}
215
216	/* If a context switch is ever initiated
217	   when the kernel holds the lock, release
218	   that lock here. */
219	clear_bit(0, &dev->context_flag);
220
221	return 0;
222}
223
224int drm_resctx(struct drm_device *dev, void *data, struct drm_file *file_priv)
225{
226	struct drm_ctx_res *res = data;
227	struct drm_ctx ctx;
228	int i;
229
230	if (res->count >= DRM_RESERVED_CONTEXTS) {
231		bzero(&ctx, sizeof(ctx));
232		for (i = 0; i < DRM_RESERVED_CONTEXTS; i++) {
233			ctx.handle = i;
234			if (DRM_COPY_TO_USER(&res->contexts[i],
235			    &ctx, sizeof(ctx)))
236				return EFAULT;
237		}
238	}
239	res->count = DRM_RESERVED_CONTEXTS;
240
241	return 0;
242}
243
244int drm_addctx(struct drm_device *dev, void *data, struct drm_file *file_priv)
245{
246	struct drm_ctx *ctx = data;
247
248	ctx->handle = drm_ctxbitmap_next(dev);
249	if (ctx->handle == DRM_KERNEL_CONTEXT) {
250		/* Skip kernel's context and get a new one. */
251		ctx->handle = drm_ctxbitmap_next(dev);
252	}
253	DRM_DEBUG("%d\n", ctx->handle);
254	if (ctx->handle == -1) {
255		DRM_DEBUG("Not enough free contexts.\n");
256		/* Should this return -EBUSY instead? */
257		return ENOMEM;
258	}
259
260	if (dev->driver->context_ctor && ctx->handle != DRM_KERNEL_CONTEXT) {
261		DRM_LOCK();
262		dev->driver->context_ctor(dev, ctx->handle);
263		DRM_UNLOCK();
264	}
265
266	return 0;
267}
268
269int drm_modctx(struct drm_device *dev, void *data, struct drm_file *file_priv)
270{
271	/* This does nothing */
272	return 0;
273}
274
275int drm_getctx(struct drm_device *dev, void *data, struct drm_file *file_priv)
276{
277	struct drm_ctx *ctx = data;
278
279	/* This is 0, because we don't handle any context flags */
280	ctx->flags = 0;
281
282	return 0;
283}
284
285int drm_switchctx(struct drm_device *dev, void *data,
286		  struct drm_file *file_priv)
287{
288	struct drm_ctx *ctx = data;
289
290	DRM_DEBUG("%d\n", ctx->handle);
291	return drm_context_switch(dev, dev->last_context, ctx->handle);
292}
293
294int drm_newctx(struct drm_device *dev, void *data, struct drm_file *file_priv)
295{
296	struct drm_ctx *ctx = data;
297
298	DRM_DEBUG("%d\n", ctx->handle);
299	drm_context_switch_complete(dev, ctx->handle);
300
301	return 0;
302}
303
304int drm_rmctx(struct drm_device *dev, void *data, struct drm_file *file_priv)
305{
306	struct drm_ctx *ctx = data;
307
308	DRM_DEBUG("%d\n", ctx->handle);
309	if (ctx->handle != DRM_KERNEL_CONTEXT) {
310		if (dev->driver->context_dtor) {
311			DRM_LOCK();
312			dev->driver->context_dtor(dev, ctx->handle);
313			DRM_UNLOCK();
314		}
315
316		drm_ctxbitmap_free(dev, ctx->handle);
317	}
318
319	return 0;
320}
321