nouveau.c revision 857b0bc6
1e88f27b3Smrg/*
2e88f27b3Smrg * Copyright 2012 Red Hat Inc.
3e88f27b3Smrg *
4e88f27b3Smrg * Permission is hereby granted, free of charge, to any person obtaining a
5e88f27b3Smrg * copy of this software and associated documentation files (the "Software"),
6e88f27b3Smrg * to deal in the Software without restriction, including without limitation
7e88f27b3Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8e88f27b3Smrg * and/or sell copies of the Software, and to permit persons to whom the
9e88f27b3Smrg * Software is furnished to do so, subject to the following conditions:
10e88f27b3Smrg *
11e88f27b3Smrg * The above copyright notice and this permission notice shall be included in
12e88f27b3Smrg * all copies or substantial portions of the Software.
13e88f27b3Smrg *
14e88f27b3Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15e88f27b3Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16e88f27b3Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17e88f27b3Smrg * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18e88f27b3Smrg * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19e88f27b3Smrg * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20e88f27b3Smrg * OTHER DEALINGS IN THE SOFTWARE.
21e88f27b3Smrg *
22e88f27b3Smrg * Authors: Ben Skeggs
23e88f27b3Smrg */
24e88f27b3Smrg
25e88f27b3Smrg#ifdef HAVE_CONFIG_H
26e88f27b3Smrg#include <config.h>
27e88f27b3Smrg#endif
28e88f27b3Smrg
29e88f27b3Smrg#include <stdio.h>
30e88f27b3Smrg#include <stdlib.h>
31e88f27b3Smrg#include <stdint.h>
32e88f27b3Smrg#include <string.h>
33e88f27b3Smrg#include <stdbool.h>
34e88f27b3Smrg#include <assert.h>
35e88f27b3Smrg#include <errno.h>
36e88f27b3Smrg#include <sys/mman.h>
37e88f27b3Smrg#include <fcntl.h>
38e88f27b3Smrg
39e88f27b3Smrg#include <xf86drm.h>
40e88f27b3Smrg#include <xf86atomic.h>
41e88f27b3Smrg#include "libdrm_lists.h"
42e88f27b3Smrg#include "nouveau_drm.h"
43e88f27b3Smrg
44e88f27b3Smrg#include "nouveau.h"
45e88f27b3Smrg#include "private.h"
46e88f27b3Smrg
47e88f27b3Smrg#ifdef DEBUG
48e88f27b3Smrguint32_t nouveau_debug = 0;
49e88f27b3Smrg
50e88f27b3Smrgstatic void
51e88f27b3Smrgdebug_init(char *args)
52e88f27b3Smrg{
53e88f27b3Smrg	if (args) {
54e88f27b3Smrg		int n = strtol(args, NULL, 0);
55e88f27b3Smrg		if (n >= 0)
56e88f27b3Smrg			nouveau_debug = n;
57e88f27b3Smrg	}
58e88f27b3Smrg}
59e88f27b3Smrg#endif
60e88f27b3Smrg
61e88f27b3Smrg/* this is the old libdrm's version of nouveau_device_wrap(), the symbol
62e88f27b3Smrg * is kept here to prevent AIGLX from crashing if the DDX is linked against
63e88f27b3Smrg * the new libdrm, but the DRI driver against the old
64e88f27b3Smrg */
65e88f27b3Smrgint
66e88f27b3Smrgnouveau_device_open_existing(struct nouveau_device **pdev, int close, int fd,
67e88f27b3Smrg			     drm_context_t ctx)
68e88f27b3Smrg{
69e88f27b3Smrg	return -EACCES;
70e88f27b3Smrg}
71e88f27b3Smrg
72e88f27b3Smrgint
73e88f27b3Smrgnouveau_device_wrap(int fd, int close, struct nouveau_device **pdev)
74e88f27b3Smrg{
75e88f27b3Smrg	struct nouveau_device_priv *nvdev = calloc(1, sizeof(*nvdev));
76e88f27b3Smrg	struct nouveau_device *dev = &nvdev->base;
77e88f27b3Smrg	uint64_t chipset, vram, gart, bousage;
78e88f27b3Smrg	drmVersionPtr ver;
79e88f27b3Smrg	int ret;
80e88f27b3Smrg	char *tmp;
81e88f27b3Smrg
82e88f27b3Smrg#ifdef DEBUG
83e88f27b3Smrg	debug_init(getenv("NOUVEAU_LIBDRM_DEBUG"));
84e88f27b3Smrg#endif
85e88f27b3Smrg
86e88f27b3Smrg	if (!nvdev)
87e88f27b3Smrg		return -ENOMEM;
88857b0bc6Smrg	ret = pthread_mutex_init(&nvdev->lock, NULL);
89857b0bc6Smrg	if (ret) {
90857b0bc6Smrg		free(nvdev);
91857b0bc6Smrg		return ret;
92857b0bc6Smrg	}
93857b0bc6Smrg
94e88f27b3Smrg	nvdev->base.fd = fd;
95e88f27b3Smrg
96e88f27b3Smrg	ver = drmGetVersion(fd);
97e88f27b3Smrg	if (ver) dev->drm_version = (ver->version_major << 24) |
98e88f27b3Smrg				    (ver->version_minor << 8) |
99e88f27b3Smrg				     ver->version_patchlevel;
100e88f27b3Smrg	drmFreeVersion(ver);
101e88f27b3Smrg
102e88f27b3Smrg	if ( dev->drm_version != 0x00000010 &&
103e88f27b3Smrg	    (dev->drm_version <  0x01000000 ||
104e88f27b3Smrg	     dev->drm_version >= 0x02000000)) {
105e88f27b3Smrg		nouveau_device_del(&dev);
106e88f27b3Smrg		return -EINVAL;
107e88f27b3Smrg	}
108e88f27b3Smrg
109e88f27b3Smrg	ret = nouveau_getparam(dev, NOUVEAU_GETPARAM_CHIPSET_ID, &chipset);
110e88f27b3Smrg	if (ret == 0)
111e88f27b3Smrg	ret = nouveau_getparam(dev, NOUVEAU_GETPARAM_FB_SIZE, &vram);
112e88f27b3Smrg	if (ret == 0)
113e88f27b3Smrg	ret = nouveau_getparam(dev, NOUVEAU_GETPARAM_AGP_SIZE, &gart);
114e88f27b3Smrg	if (ret) {
115e88f27b3Smrg		nouveau_device_del(&dev);
116e88f27b3Smrg		return ret;
117e88f27b3Smrg	}
118e88f27b3Smrg
119e88f27b3Smrg	ret = nouveau_getparam(dev, NOUVEAU_GETPARAM_HAS_BO_USAGE, &bousage);
120e88f27b3Smrg	if (ret == 0)
121e88f27b3Smrg		nvdev->have_bo_usage = (bousage != 0);
122e88f27b3Smrg
123e88f27b3Smrg	nvdev->close = close;
124e88f27b3Smrg
125e88f27b3Smrg	tmp = getenv("NOUVEAU_LIBDRM_VRAM_LIMIT_PERCENT");
126e88f27b3Smrg	if (tmp)
127e88f27b3Smrg		nvdev->vram_limit_percent = atoi(tmp);
128e88f27b3Smrg	else
129e88f27b3Smrg		nvdev->vram_limit_percent = 80;
130e88f27b3Smrg	tmp = getenv("NOUVEAU_LIBDRM_GART_LIMIT_PERCENT");
131e88f27b3Smrg	if (tmp)
132e88f27b3Smrg		nvdev->gart_limit_percent = atoi(tmp);
133e88f27b3Smrg	else
134e88f27b3Smrg		nvdev->gart_limit_percent = 80;
135e88f27b3Smrg	DRMINITLISTHEAD(&nvdev->bo_list);
136e88f27b3Smrg	nvdev->base.object.oclass = NOUVEAU_DEVICE_CLASS;
137e88f27b3Smrg	nvdev->base.lib_version = 0x01000000;
138e88f27b3Smrg	nvdev->base.chipset = chipset;
139e88f27b3Smrg	nvdev->base.vram_size = vram;
140e88f27b3Smrg	nvdev->base.gart_size = gart;
141e88f27b3Smrg	nvdev->base.vram_limit =
142e88f27b3Smrg		(nvdev->base.vram_size * nvdev->vram_limit_percent) / 100;
143e88f27b3Smrg	nvdev->base.gart_limit =
144e88f27b3Smrg		(nvdev->base.gart_size * nvdev->gart_limit_percent) / 100;
145e88f27b3Smrg
146e88f27b3Smrg	*pdev = &nvdev->base;
147e88f27b3Smrg	return 0;
148e88f27b3Smrg}
149e88f27b3Smrg
150e88f27b3Smrgint
151e88f27b3Smrgnouveau_device_open(const char *busid, struct nouveau_device **pdev)
152e88f27b3Smrg{
153e88f27b3Smrg	int ret = -ENODEV, fd = drmOpen("nouveau", busid);
154e88f27b3Smrg	if (fd >= 0) {
155e88f27b3Smrg		ret = nouveau_device_wrap(fd, 1, pdev);
156e88f27b3Smrg		if (ret)
157e88f27b3Smrg			drmClose(fd);
158e88f27b3Smrg	}
159e88f27b3Smrg	return ret;
160e88f27b3Smrg}
161e88f27b3Smrg
162e88f27b3Smrgvoid
163e88f27b3Smrgnouveau_device_del(struct nouveau_device **pdev)
164e88f27b3Smrg{
165e88f27b3Smrg	struct nouveau_device_priv *nvdev = nouveau_device(*pdev);
166e88f27b3Smrg	if (nvdev) {
167e88f27b3Smrg		if (nvdev->close)
168e88f27b3Smrg			drmClose(nvdev->base.fd);
169e88f27b3Smrg		free(nvdev->client);
170857b0bc6Smrg		pthread_mutex_destroy(&nvdev->lock);
171e88f27b3Smrg		free(nvdev);
172e88f27b3Smrg		*pdev = NULL;
173e88f27b3Smrg	}
174e88f27b3Smrg}
175e88f27b3Smrg
176e88f27b3Smrgint
177e88f27b3Smrgnouveau_getparam(struct nouveau_device *dev, uint64_t param, uint64_t *value)
178e88f27b3Smrg{
179e88f27b3Smrg	struct drm_nouveau_getparam r = { param, 0 };
180e88f27b3Smrg	int fd = dev->fd, ret =
181e88f27b3Smrg		drmCommandWriteRead(fd, DRM_NOUVEAU_GETPARAM, &r, sizeof(r));
182e88f27b3Smrg	*value = r.value;
183e88f27b3Smrg	return ret;
184e88f27b3Smrg}
185e88f27b3Smrg
186e88f27b3Smrgint
187e88f27b3Smrgnouveau_setparam(struct nouveau_device *dev, uint64_t param, uint64_t value)
188e88f27b3Smrg{
189e88f27b3Smrg	struct drm_nouveau_setparam r = { param, value };
190e88f27b3Smrg	return drmCommandWrite(dev->fd, DRM_NOUVEAU_SETPARAM, &r, sizeof(r));
191e88f27b3Smrg}
192e88f27b3Smrg
193e88f27b3Smrgint
194e88f27b3Smrgnouveau_client_new(struct nouveau_device *dev, struct nouveau_client **pclient)
195e88f27b3Smrg{
196e88f27b3Smrg	struct nouveau_device_priv *nvdev = nouveau_device(dev);
197e88f27b3Smrg	struct nouveau_client_priv *pcli;
198e88f27b3Smrg	int id = 0, i, ret = -ENOMEM;
199e88f27b3Smrg	uint32_t *clients;
200e88f27b3Smrg
201857b0bc6Smrg	pthread_mutex_lock(&nvdev->lock);
202857b0bc6Smrg
203e88f27b3Smrg	for (i = 0; i < nvdev->nr_client; i++) {
204e88f27b3Smrg		id = ffs(nvdev->client[i]) - 1;
205e88f27b3Smrg		if (id >= 0)
206e88f27b3Smrg			goto out;
207e88f27b3Smrg	}
208e88f27b3Smrg
209e88f27b3Smrg	clients = realloc(nvdev->client, sizeof(uint32_t) * (i + 1));
210e88f27b3Smrg	if (!clients)
211857b0bc6Smrg		goto unlock;
212e88f27b3Smrg	nvdev->client = clients;
213e88f27b3Smrg	nvdev->client[i] = 0;
214e88f27b3Smrg	nvdev->nr_client++;
215e88f27b3Smrg
216e88f27b3Smrgout:
217e88f27b3Smrg	pcli = calloc(1, sizeof(*pcli));
218e88f27b3Smrg	if (pcli) {
219e88f27b3Smrg		nvdev->client[i] |= (1 << id);
220e88f27b3Smrg		pcli->base.device = dev;
221e88f27b3Smrg		pcli->base.id = (i * 32) + id;
222e88f27b3Smrg		ret = 0;
223e88f27b3Smrg	}
224e88f27b3Smrg
225e88f27b3Smrg	*pclient = &pcli->base;
226857b0bc6Smrg
227857b0bc6Smrgunlock:
228857b0bc6Smrg	pthread_mutex_unlock(&nvdev->lock);
229e88f27b3Smrg	return ret;
230e88f27b3Smrg}
231e88f27b3Smrg
232e88f27b3Smrgvoid
233e88f27b3Smrgnouveau_client_del(struct nouveau_client **pclient)
234e88f27b3Smrg{
235e88f27b3Smrg	struct nouveau_client_priv *pcli = nouveau_client(*pclient);
236e88f27b3Smrg	struct nouveau_device_priv *nvdev;
237e88f27b3Smrg	if (pcli) {
238e88f27b3Smrg		int id = pcli->base.id;
239e88f27b3Smrg		nvdev = nouveau_device(pcli->base.device);
240857b0bc6Smrg		pthread_mutex_lock(&nvdev->lock);
241e88f27b3Smrg		nvdev->client[id / 32] &= ~(1 << (id % 32));
242857b0bc6Smrg		pthread_mutex_unlock(&nvdev->lock);
243e88f27b3Smrg		free(pcli->kref);
244e88f27b3Smrg		free(pcli);
245e88f27b3Smrg	}
246e88f27b3Smrg}
247e88f27b3Smrg
248e88f27b3Smrgint
249e88f27b3Smrgnouveau_object_new(struct nouveau_object *parent, uint64_t handle,
250e88f27b3Smrg		   uint32_t oclass, void *data, uint32_t length,
251e88f27b3Smrg		   struct nouveau_object **pobj)
252e88f27b3Smrg{
253e88f27b3Smrg	struct nouveau_device *dev;
254e88f27b3Smrg	struct nouveau_object *obj;
255e88f27b3Smrg	int ret = -EINVAL;
256e88f27b3Smrg
257e88f27b3Smrg	if (length == 0)
258e88f27b3Smrg		length = sizeof(struct nouveau_object *);
259e88f27b3Smrg	obj = malloc(sizeof(*obj) + length);
260e88f27b3Smrg	obj->parent = parent;
261e88f27b3Smrg	obj->handle = handle;
262e88f27b3Smrg	obj->oclass = oclass;
263e88f27b3Smrg	obj->length = length;
264e88f27b3Smrg	obj->data = obj + 1;
265e88f27b3Smrg	if (data)
266e88f27b3Smrg		memcpy(obj->data, data, length);
267e88f27b3Smrg	*(struct nouveau_object **)obj->data = obj;
268e88f27b3Smrg
269e88f27b3Smrg	dev = nouveau_object_find(obj, NOUVEAU_DEVICE_CLASS);
270e88f27b3Smrg	switch (parent->oclass) {
271e88f27b3Smrg	case NOUVEAU_DEVICE_CLASS:
272e88f27b3Smrg		switch (obj->oclass) {
273e88f27b3Smrg		case NOUVEAU_FIFO_CHANNEL_CLASS:
274e88f27b3Smrg		{
275e88f27b3Smrg			if (dev->chipset < 0xc0)
276e88f27b3Smrg				ret = abi16_chan_nv04(obj);
277e88f27b3Smrg			else
278e88f27b3Smrg			if (dev->chipset < 0xe0)
279e88f27b3Smrg				ret = abi16_chan_nvc0(obj);
280e88f27b3Smrg			else
281e88f27b3Smrg				ret = abi16_chan_nve0(obj);
282e88f27b3Smrg		}
283e88f27b3Smrg			break;
284e88f27b3Smrg		default:
285e88f27b3Smrg			break;
286e88f27b3Smrg		}
287e88f27b3Smrg		break;
288e88f27b3Smrg	case NOUVEAU_FIFO_CHANNEL_CLASS:
289e88f27b3Smrg		switch (obj->oclass) {
290e88f27b3Smrg		case NOUVEAU_NOTIFIER_CLASS:
291e88f27b3Smrg			ret = abi16_ntfy(obj);
292e88f27b3Smrg			break;
293e88f27b3Smrg		default:
294e88f27b3Smrg			ret = abi16_engobj(obj);
295e88f27b3Smrg			break;
296e88f27b3Smrg		}
297e88f27b3Smrg	default:
298e88f27b3Smrg		break;
299e88f27b3Smrg	}
300e88f27b3Smrg
301e88f27b3Smrg	if (ret) {
302e88f27b3Smrg		free(obj);
303e88f27b3Smrg		return ret;
304e88f27b3Smrg	}
305e88f27b3Smrg
306e88f27b3Smrg	*pobj = obj;
307e88f27b3Smrg	return 0;
308e88f27b3Smrg}
309e88f27b3Smrg
310e88f27b3Smrgvoid
311e88f27b3Smrgnouveau_object_del(struct nouveau_object **pobj)
312e88f27b3Smrg{
313e88f27b3Smrg	struct nouveau_object *obj = *pobj;
314e88f27b3Smrg	struct nouveau_device *dev;
315e88f27b3Smrg	if (obj) {
316e88f27b3Smrg		dev = nouveau_object_find(obj, NOUVEAU_DEVICE_CLASS);
317e88f27b3Smrg		if (obj->oclass == NOUVEAU_FIFO_CHANNEL_CLASS) {
318e88f27b3Smrg			struct drm_nouveau_channel_free req;
319e88f27b3Smrg			req.channel = obj->handle;
320e88f27b3Smrg			drmCommandWrite(dev->fd, DRM_NOUVEAU_CHANNEL_FREE,
321e88f27b3Smrg					&req, sizeof(req));
322e88f27b3Smrg		} else {
323e88f27b3Smrg			struct drm_nouveau_gpuobj_free req;
324e88f27b3Smrg			req.channel = obj->parent->handle;
325e88f27b3Smrg			req.handle  = obj->handle;
326e88f27b3Smrg			drmCommandWrite(dev->fd, DRM_NOUVEAU_GPUOBJ_FREE,
327e88f27b3Smrg					&req, sizeof(req));
328e88f27b3Smrg		}
329e88f27b3Smrg	}
330e88f27b3Smrg	free(obj);
331e88f27b3Smrg	*pobj = NULL;
332e88f27b3Smrg}
333e88f27b3Smrg
334e88f27b3Smrgvoid *
335e88f27b3Smrgnouveau_object_find(struct nouveau_object *obj, uint32_t pclass)
336e88f27b3Smrg{
337e88f27b3Smrg	while (obj && obj->oclass != pclass) {
338e88f27b3Smrg		obj = obj->parent;
339e88f27b3Smrg		if (pclass == NOUVEAU_PARENT_CLASS)
340e88f27b3Smrg			break;
341e88f27b3Smrg	}
342e88f27b3Smrg	return obj;
343e88f27b3Smrg}
344e88f27b3Smrg
345e88f27b3Smrgstatic void
346e88f27b3Smrgnouveau_bo_del(struct nouveau_bo *bo)
347e88f27b3Smrg{
348857b0bc6Smrg	struct nouveau_device_priv *nvdev = nouveau_device(bo->device);
349e88f27b3Smrg	struct nouveau_bo_priv *nvbo = nouveau_bo(bo);
350e88f27b3Smrg	struct drm_gem_close req = { bo->handle };
351857b0bc6Smrg
352857b0bc6Smrg	pthread_mutex_lock(&nvdev->lock);
353857b0bc6Smrg	if (nvbo->name) {
354857b0bc6Smrg		if (atomic_read(&nvbo->refcnt)) {
355857b0bc6Smrg			/*
356857b0bc6Smrg			 * bo has been revived by a race with
357857b0bc6Smrg			 * nouveau_bo_prime_handle_ref, or nouveau_bo_name_ref.
358857b0bc6Smrg			 *
359857b0bc6Smrg			 * In theory there's still a race possible with
360857b0bc6Smrg			 * nouveau_bo_wrap, but when using this function
361857b0bc6Smrg			 * the lifetime of the handle is probably already
362857b0bc6Smrg			 * handled in another way. If there are races
363857b0bc6Smrg			 * you're probably using nouveau_bo_wrap wrong.
364857b0bc6Smrg			 */
365857b0bc6Smrg			pthread_mutex_unlock(&nvdev->lock);
366857b0bc6Smrg			return;
367857b0bc6Smrg		}
368857b0bc6Smrg		DRMLISTDEL(&nvbo->head);
369857b0bc6Smrg		/*
370857b0bc6Smrg		 * This bo has to be closed with the lock held because gem
371857b0bc6Smrg		 * handles are not refcounted. If a shared bo is closed and
372857b0bc6Smrg		 * re-opened in another thread a race against
373857b0bc6Smrg		 * DRM_IOCTL_GEM_OPEN or drmPrimeFDToHandle might cause the
374857b0bc6Smrg		 * bo to be closed accidentally while re-importing.
375857b0bc6Smrg		 */
376857b0bc6Smrg		drmIoctl(bo->device->fd, DRM_IOCTL_GEM_CLOSE, &req);
377857b0bc6Smrg		pthread_mutex_unlock(&nvdev->lock);
378857b0bc6Smrg	} else {
379857b0bc6Smrg		DRMLISTDEL(&nvbo->head);
380857b0bc6Smrg		pthread_mutex_unlock(&nvdev->lock);
381857b0bc6Smrg		drmIoctl(bo->device->fd, DRM_IOCTL_GEM_CLOSE, &req);
382857b0bc6Smrg	}
383e88f27b3Smrg	if (bo->map)
384e88f27b3Smrg		munmap(bo->map, bo->size);
385e88f27b3Smrg	free(nvbo);
386e88f27b3Smrg}
387e88f27b3Smrg
388e88f27b3Smrgint
389e88f27b3Smrgnouveau_bo_new(struct nouveau_device *dev, uint32_t flags, uint32_t align,
390e88f27b3Smrg	       uint64_t size, union nouveau_bo_config *config,
391e88f27b3Smrg	       struct nouveau_bo **pbo)
392e88f27b3Smrg{
393e88f27b3Smrg	struct nouveau_device_priv *nvdev = nouveau_device(dev);
394e88f27b3Smrg	struct nouveau_bo_priv *nvbo = calloc(1, sizeof(*nvbo));
395e88f27b3Smrg	struct nouveau_bo *bo = &nvbo->base;
396e88f27b3Smrg	int ret;
397e88f27b3Smrg
398e88f27b3Smrg	if (!nvbo)
399e88f27b3Smrg		return -ENOMEM;
400e88f27b3Smrg	atomic_set(&nvbo->refcnt, 1);
401e88f27b3Smrg	bo->device = dev;
402e88f27b3Smrg	bo->flags = flags;
403e88f27b3Smrg	bo->size = size;
404e88f27b3Smrg
405e88f27b3Smrg	ret = abi16_bo_init(bo, align, config);
406e88f27b3Smrg	if (ret) {
407e88f27b3Smrg		free(nvbo);
408e88f27b3Smrg		return ret;
409e88f27b3Smrg	}
410e88f27b3Smrg
411857b0bc6Smrg	pthread_mutex_lock(&nvdev->lock);
412e88f27b3Smrg	DRMLISTADD(&nvbo->head, &nvdev->bo_list);
413857b0bc6Smrg	pthread_mutex_unlock(&nvdev->lock);
414e88f27b3Smrg
415e88f27b3Smrg	*pbo = bo;
416e88f27b3Smrg	return 0;
417e88f27b3Smrg}
418e88f27b3Smrg
419857b0bc6Smrgstatic int
420857b0bc6Smrgnouveau_bo_wrap_locked(struct nouveau_device *dev, uint32_t handle,
421857b0bc6Smrg		       struct nouveau_bo **pbo)
422e88f27b3Smrg{
423e88f27b3Smrg	struct nouveau_device_priv *nvdev = nouveau_device(dev);
424e88f27b3Smrg	struct drm_nouveau_gem_info req = { .handle = handle };
425e88f27b3Smrg	struct nouveau_bo_priv *nvbo;
426e88f27b3Smrg	int ret;
427e88f27b3Smrg
428e88f27b3Smrg	DRMLISTFOREACHENTRY(nvbo, &nvdev->bo_list, head) {
429e88f27b3Smrg		if (nvbo->base.handle == handle) {
430e88f27b3Smrg			*pbo = NULL;
431e88f27b3Smrg			nouveau_bo_ref(&nvbo->base, pbo);
432e88f27b3Smrg			return 0;
433e88f27b3Smrg		}
434e88f27b3Smrg	}
435e88f27b3Smrg
436e88f27b3Smrg	ret = drmCommandWriteRead(dev->fd, DRM_NOUVEAU_GEM_INFO,
437e88f27b3Smrg				  &req, sizeof(req));
438e88f27b3Smrg	if (ret)
439e88f27b3Smrg		return ret;
440e88f27b3Smrg
441e88f27b3Smrg	nvbo = calloc(1, sizeof(*nvbo));
442e88f27b3Smrg	if (nvbo) {
443e88f27b3Smrg		atomic_set(&nvbo->refcnt, 1);
444e88f27b3Smrg		nvbo->base.device = dev;
445e88f27b3Smrg		abi16_bo_info(&nvbo->base, &req);
446e88f27b3Smrg		DRMLISTADD(&nvbo->head, &nvdev->bo_list);
447e88f27b3Smrg		*pbo = &nvbo->base;
448e88f27b3Smrg		return 0;
449e88f27b3Smrg	}
450e88f27b3Smrg
451e88f27b3Smrg	return -ENOMEM;
452e88f27b3Smrg}
453e88f27b3Smrg
454857b0bc6Smrgint
455857b0bc6Smrgnouveau_bo_wrap(struct nouveau_device *dev, uint32_t handle,
456857b0bc6Smrg		struct nouveau_bo **pbo)
457857b0bc6Smrg{
458857b0bc6Smrg	struct nouveau_device_priv *nvdev = nouveau_device(dev);
459857b0bc6Smrg	int ret;
460857b0bc6Smrg	pthread_mutex_lock(&nvdev->lock);
461857b0bc6Smrg	ret = nouveau_bo_wrap_locked(dev, handle, pbo);
462857b0bc6Smrg	pthread_mutex_unlock(&nvdev->lock);
463857b0bc6Smrg	return ret;
464857b0bc6Smrg}
465857b0bc6Smrg
466e88f27b3Smrgint
467e88f27b3Smrgnouveau_bo_name_ref(struct nouveau_device *dev, uint32_t name,
468e88f27b3Smrg		    struct nouveau_bo **pbo)
469e88f27b3Smrg{
470e88f27b3Smrg	struct nouveau_device_priv *nvdev = nouveau_device(dev);
471e88f27b3Smrg	struct nouveau_bo_priv *nvbo;
472e88f27b3Smrg	struct drm_gem_open req = { .name = name };
473e88f27b3Smrg	int ret;
474e88f27b3Smrg
475857b0bc6Smrg	pthread_mutex_lock(&nvdev->lock);
476e88f27b3Smrg	DRMLISTFOREACHENTRY(nvbo, &nvdev->bo_list, head) {
477e88f27b3Smrg		if (nvbo->name == name) {
478e88f27b3Smrg			*pbo = NULL;
479e88f27b3Smrg			nouveau_bo_ref(&nvbo->base, pbo);
480857b0bc6Smrg			pthread_mutex_unlock(&nvdev->lock);
481e88f27b3Smrg			return 0;
482e88f27b3Smrg		}
483e88f27b3Smrg	}
484e88f27b3Smrg
485e88f27b3Smrg	ret = drmIoctl(dev->fd, DRM_IOCTL_GEM_OPEN, &req);
486e88f27b3Smrg	if (ret == 0) {
487857b0bc6Smrg		ret = nouveau_bo_wrap_locked(dev, req.handle, pbo);
488e88f27b3Smrg		nouveau_bo((*pbo))->name = name;
489857b0bc6Smrg		pthread_mutex_unlock(&nvdev->lock);
490e88f27b3Smrg	}
491e88f27b3Smrg
492e88f27b3Smrg	return ret;
493e88f27b3Smrg}
494e88f27b3Smrg
495e88f27b3Smrgint
496e88f27b3Smrgnouveau_bo_name_get(struct nouveau_bo *bo, uint32_t *name)
497e88f27b3Smrg{
498e88f27b3Smrg	struct drm_gem_flink req = { .handle = bo->handle };
499e88f27b3Smrg	struct nouveau_bo_priv *nvbo = nouveau_bo(bo);
500857b0bc6Smrg
501857b0bc6Smrg	*name = nvbo->name;
502857b0bc6Smrg	if (!*name || *name == ~0U) {
503e88f27b3Smrg		int ret = drmIoctl(bo->device->fd, DRM_IOCTL_GEM_FLINK, &req);
504857b0bc6Smrg		if (ret) {
505857b0bc6Smrg			*name = 0;
506e88f27b3Smrg			return ret;
507857b0bc6Smrg		}
508857b0bc6Smrg		nvbo->name = *name = req.name;
509e88f27b3Smrg	}
510e88f27b3Smrg	return 0;
511e88f27b3Smrg}
512e88f27b3Smrg
513e88f27b3Smrgvoid
514e88f27b3Smrgnouveau_bo_ref(struct nouveau_bo *bo, struct nouveau_bo **pref)
515e88f27b3Smrg{
516e88f27b3Smrg	struct nouveau_bo *ref = *pref;
517e88f27b3Smrg	if (bo) {
518e88f27b3Smrg		atomic_inc(&nouveau_bo(bo)->refcnt);
519e88f27b3Smrg	}
520e88f27b3Smrg	if (ref) {
521e88f27b3Smrg		if (atomic_dec_and_test(&nouveau_bo(ref)->refcnt))
522e88f27b3Smrg			nouveau_bo_del(ref);
523e88f27b3Smrg	}
524e88f27b3Smrg	*pref = bo;
525e88f27b3Smrg}
526e88f27b3Smrg
527e88f27b3Smrgint
528e88f27b3Smrgnouveau_bo_prime_handle_ref(struct nouveau_device *dev, int prime_fd,
529e88f27b3Smrg			    struct nouveau_bo **bo)
530e88f27b3Smrg{
531857b0bc6Smrg	struct nouveau_device_priv *nvdev = nouveau_device(dev);
532e88f27b3Smrg	int ret;
533e88f27b3Smrg	unsigned int handle;
534e88f27b3Smrg
535857b0bc6Smrg	nouveau_bo_ref(NULL, bo);
536e88f27b3Smrg
537857b0bc6Smrg	pthread_mutex_lock(&nvdev->lock);
538857b0bc6Smrg	ret = drmPrimeFDToHandle(dev->fd, prime_fd, &handle);
539857b0bc6Smrg	if (ret == 0) {
540857b0bc6Smrg		ret = nouveau_bo_wrap_locked(dev, handle, bo);
541857b0bc6Smrg		if (!ret) {
542857b0bc6Smrg			struct nouveau_bo_priv *nvbo = nouveau_bo(*bo);
543857b0bc6Smrg			if (!nvbo->name) {
544857b0bc6Smrg				/*
545857b0bc6Smrg				 * XXX: Force locked DRM_IOCTL_GEM_CLOSE
546857b0bc6Smrg				 * to rule out race conditions
547857b0bc6Smrg				 */
548857b0bc6Smrg				nvbo->name = ~0;
549857b0bc6Smrg			}
550857b0bc6Smrg		}
551e88f27b3Smrg	}
552857b0bc6Smrg	pthread_mutex_unlock(&nvdev->lock);
553857b0bc6Smrg	return ret;
554e88f27b3Smrg}
555e88f27b3Smrg
556e88f27b3Smrgint
557e88f27b3Smrgnouveau_bo_set_prime(struct nouveau_bo *bo, int *prime_fd)
558e88f27b3Smrg{
559e88f27b3Smrg	struct nouveau_bo_priv *nvbo = nouveau_bo(bo);
560e88f27b3Smrg	int ret;
561e88f27b3Smrg
562e88f27b3Smrg	ret = drmPrimeHandleToFD(bo->device->fd, nvbo->base.handle, DRM_CLOEXEC, prime_fd);
563e88f27b3Smrg	if (ret)
564e88f27b3Smrg		return ret;
565857b0bc6Smrg	if (!nvbo->name)
566857b0bc6Smrg		nvbo->name = ~0;
567e88f27b3Smrg	return 0;
568e88f27b3Smrg}
569e88f27b3Smrg
570e88f27b3Smrgint
571e88f27b3Smrgnouveau_bo_wait(struct nouveau_bo *bo, uint32_t access,
572e88f27b3Smrg		struct nouveau_client *client)
573e88f27b3Smrg{
574e88f27b3Smrg	struct nouveau_bo_priv *nvbo = nouveau_bo(bo);
575e88f27b3Smrg	struct drm_nouveau_gem_cpu_prep req;
576e88f27b3Smrg	struct nouveau_pushbuf *push;
577e88f27b3Smrg	int ret = 0;
578e88f27b3Smrg
579e88f27b3Smrg	if (!(access & NOUVEAU_BO_RDWR))
580e88f27b3Smrg		return 0;
581e88f27b3Smrg
582e88f27b3Smrg	push = cli_push_get(client, bo);
583e88f27b3Smrg	if (push && push->channel)
584e88f27b3Smrg		nouveau_pushbuf_kick(push, push->channel);
585e88f27b3Smrg
586e88f27b3Smrg	if (!nvbo->name && !(nvbo->access & NOUVEAU_BO_WR) &&
587e88f27b3Smrg			   !(      access & NOUVEAU_BO_WR))
588e88f27b3Smrg		return 0;
589e88f27b3Smrg
590e88f27b3Smrg	req.handle = bo->handle;
591e88f27b3Smrg	req.flags = 0;
592e88f27b3Smrg	if (access & NOUVEAU_BO_WR)
593e88f27b3Smrg		req.flags |= NOUVEAU_GEM_CPU_PREP_WRITE;
594e88f27b3Smrg	if (access & NOUVEAU_BO_NOBLOCK)
595e88f27b3Smrg		req.flags |= NOUVEAU_GEM_CPU_PREP_NOWAIT;
596e88f27b3Smrg
597e88f27b3Smrg	ret = drmCommandWrite(bo->device->fd, DRM_NOUVEAU_GEM_CPU_PREP,
598e88f27b3Smrg			      &req, sizeof(req));
599e88f27b3Smrg	if (ret == 0)
600e88f27b3Smrg		nvbo->access = 0;
601e88f27b3Smrg	return ret;
602e88f27b3Smrg}
603e88f27b3Smrg
604e88f27b3Smrgint
605e88f27b3Smrgnouveau_bo_map(struct nouveau_bo *bo, uint32_t access,
606e88f27b3Smrg	       struct nouveau_client *client)
607e88f27b3Smrg{
608e88f27b3Smrg	struct nouveau_bo_priv *nvbo = nouveau_bo(bo);
609e88f27b3Smrg	if (bo->map == NULL) {
610e88f27b3Smrg		bo->map = mmap(0, bo->size, PROT_READ | PROT_WRITE,
611e88f27b3Smrg			       MAP_SHARED, bo->device->fd, nvbo->map_handle);
612e88f27b3Smrg		if (bo->map == MAP_FAILED) {
613e88f27b3Smrg			bo->map = NULL;
614e88f27b3Smrg			return -errno;
615e88f27b3Smrg		}
616e88f27b3Smrg	}
617e88f27b3Smrg	return nouveau_bo_wait(bo, access, client);
618e88f27b3Smrg}
619