qxl_release.c revision 6d01f1f54c01877baad11389291c1aeab9c11e0f
1/*
2 * Copyright 2011 Red Hat, Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * on the rights to use, copy, modify, merge, publish, distribute, sub
8 * license, and/or sell copies of the Software, and to permit persons to whom
9 * the Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.  IN NO EVENT SHALL
18 * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 */
22#include "qxl_drv.h"
23#include "qxl_object.h"
24
25/*
26 * drawable cmd cache - allocate a bunch of VRAM pages, suballocate
27 * into 256 byte chunks for now - gives 16 cmds per page.
28 *
29 * use an ida to index into the chunks?
30 */
31/* manage releaseables */
32/* stack them 16 high for now -drawable object is 191 */
33#define RELEASE_SIZE 256
34#define RELEASES_PER_BO (4096 / RELEASE_SIZE)
35/* put an alloc/dealloc surface cmd into one bo and round up to 128 */
36#define SURFACE_RELEASE_SIZE 128
37#define SURFACE_RELEASES_PER_BO (4096 / SURFACE_RELEASE_SIZE)
38
39static const int release_size_per_bo[] = { RELEASE_SIZE, SURFACE_RELEASE_SIZE, RELEASE_SIZE };
40static const int releases_per_bo[] = { RELEASES_PER_BO, SURFACE_RELEASES_PER_BO, RELEASES_PER_BO };
41uint64_t
42qxl_release_alloc(struct qxl_device *qdev, int type,
43		  struct qxl_release **ret)
44{
45	struct qxl_release *release;
46	int handle = 0;
47	size_t size = sizeof(*release);
48	int idr_ret;
49
50	release = kmalloc(size, GFP_KERNEL);
51	if (!release) {
52		DRM_ERROR("Out of memory\n");
53		return 0;
54	}
55	release->type = type;
56	release->bo_count = 0;
57	release->release_offset = 0;
58	release->surface_release_id = 0;
59again:
60	if (idr_pre_get(&qdev->release_idr, GFP_KERNEL) == 0) {
61		DRM_ERROR("Out of memory for release idr\n");
62		kfree(release);
63		goto release_fail;
64	}
65	spin_lock(&qdev->release_idr_lock);
66	idr_ret = idr_get_new_above(&qdev->release_idr, release, 1, &handle);
67	spin_unlock(&qdev->release_idr_lock);
68	if (idr_ret == -EAGAIN)
69		goto again;
70	if (ret)
71		*ret = release;
72	QXL_INFO(qdev, "allocated release %lld\n", handle);
73	release->id = handle;
74release_fail:
75
76	return handle;
77}
78
79void
80qxl_release_free(struct qxl_device *qdev,
81		 struct qxl_release *release)
82{
83	int i;
84
85	QXL_INFO(qdev, "release %d, type %d, %d bos\n", release->id,
86		 release->type, release->bo_count);
87
88	if (release->surface_release_id)
89		qxl_surface_id_dealloc(qdev, release->surface_release_id);
90
91	for (i = 0 ; i < release->bo_count; ++i) {
92		QXL_INFO(qdev, "release %llx\n",
93			release->bos[i]->tbo.addr_space_offset
94						- DRM_FILE_OFFSET);
95		qxl_fence_remove_release(&release->bos[i]->fence, release->id);
96		qxl_bo_unref(&release->bos[i]);
97	}
98	spin_lock(&qdev->release_idr_lock);
99	idr_remove(&qdev->release_idr, release->id);
100	spin_unlock(&qdev->release_idr_lock);
101	kfree(release);
102}
103
104void
105qxl_release_add_res(struct qxl_device *qdev, struct qxl_release *release,
106		    struct qxl_bo *bo)
107{
108	int i;
109	for (i = 0; i < release->bo_count; i++)
110		if (release->bos[i] == bo)
111			return;
112
113	if (release->bo_count >= QXL_MAX_RES) {
114		DRM_ERROR("exceeded max resource on a qxl_release item\n");
115		return;
116	}
117	release->bos[release->bo_count++] = qxl_bo_ref(bo);
118}
119
120static int qxl_release_bo_alloc(struct qxl_device *qdev,
121				struct qxl_bo **bo)
122{
123	int ret;
124	ret = qxl_bo_create(qdev, PAGE_SIZE, false, QXL_GEM_DOMAIN_VRAM, NULL,
125			    bo);
126	return ret;
127}
128
129int qxl_release_reserve(struct qxl_device *qdev,
130			struct qxl_release *release, bool no_wait)
131{
132	int ret;
133	if (atomic_inc_return(&release->bos[0]->reserve_count) == 1) {
134		ret = qxl_bo_reserve(release->bos[0], no_wait);
135		if (ret)
136			return ret;
137	}
138	return 0;
139}
140
141void qxl_release_unreserve(struct qxl_device *qdev,
142			  struct qxl_release *release)
143{
144	if (atomic_dec_and_test(&release->bos[0]->reserve_count))
145		qxl_bo_unreserve(release->bos[0]);
146}
147
148int qxl_alloc_surface_release_reserved(struct qxl_device *qdev,
149				       enum qxl_surface_cmd_type surface_cmd_type,
150				       struct qxl_release *create_rel,
151				       struct qxl_release **release)
152{
153	int ret;
154
155	if (surface_cmd_type == QXL_SURFACE_CMD_DESTROY && create_rel) {
156		int idr_ret;
157		struct qxl_bo *bo;
158		union qxl_release_info *info;
159
160		/* stash the release after the create command */
161		idr_ret = qxl_release_alloc(qdev, QXL_RELEASE_SURFACE_CMD, release);
162		bo = qxl_bo_ref(create_rel->bos[0]);
163
164		(*release)->release_offset = create_rel->release_offset + 64;
165
166		qxl_release_add_res(qdev, *release, bo);
167
168		ret = qxl_release_reserve(qdev, *release, false);
169		if (ret) {
170			DRM_ERROR("release reserve failed\n");
171			goto out_unref;
172		}
173		info = qxl_release_map(qdev, *release);
174		info->id = idr_ret;
175		qxl_release_unmap(qdev, *release, info);
176
177
178out_unref:
179		qxl_bo_unref(&bo);
180		return ret;
181	}
182
183	return qxl_alloc_release_reserved(qdev, sizeof(struct qxl_surface_cmd),
184					 QXL_RELEASE_SURFACE_CMD, release, NULL);
185}
186
187int qxl_alloc_release_reserved(struct qxl_device *qdev, unsigned long size,
188				       int type, struct qxl_release **release,
189				       struct qxl_bo **rbo)
190{
191	struct qxl_bo *bo;
192	int idr_ret;
193	int ret;
194	union qxl_release_info *info;
195	int cur_idx;
196
197	if (type == QXL_RELEASE_DRAWABLE)
198		cur_idx = 0;
199	else if (type == QXL_RELEASE_SURFACE_CMD)
200		cur_idx = 1;
201	else if (type == QXL_RELEASE_CURSOR_CMD)
202		cur_idx = 2;
203	else {
204		DRM_ERROR("got illegal type: %d\n", type);
205		return -EINVAL;
206	}
207
208	idr_ret = qxl_release_alloc(qdev, type, release);
209
210	mutex_lock(&qdev->release_mutex);
211	if (qdev->current_release_bo_offset[cur_idx] + 1 >= releases_per_bo[cur_idx]) {
212		qxl_bo_unref(&qdev->current_release_bo[cur_idx]);
213		qdev->current_release_bo_offset[cur_idx] = 0;
214		qdev->current_release_bo[cur_idx] = NULL;
215	}
216	if (!qdev->current_release_bo[cur_idx]) {
217		ret = qxl_release_bo_alloc(qdev, &qdev->current_release_bo[cur_idx]);
218		if (ret) {
219			mutex_unlock(&qdev->release_mutex);
220			return ret;
221		}
222
223		/* pin releases bo's they are too messy to evict */
224		ret = qxl_bo_reserve(qdev->current_release_bo[cur_idx], false);
225		qxl_bo_pin(qdev->current_release_bo[cur_idx], QXL_GEM_DOMAIN_VRAM, NULL);
226		qxl_bo_unreserve(qdev->current_release_bo[cur_idx]);
227	}
228
229	bo = qxl_bo_ref(qdev->current_release_bo[cur_idx]);
230
231	(*release)->release_offset = qdev->current_release_bo_offset[cur_idx] * release_size_per_bo[cur_idx];
232	qdev->current_release_bo_offset[cur_idx]++;
233
234	if (rbo)
235		*rbo = bo;
236
237	qxl_release_add_res(qdev, *release, bo);
238
239	ret = qxl_release_reserve(qdev, *release, false);
240	mutex_unlock(&qdev->release_mutex);
241	if (ret)
242		goto out_unref;
243
244	info = qxl_release_map(qdev, *release);
245	info->id = idr_ret;
246	qxl_release_unmap(qdev, *release, info);
247
248out_unref:
249	qxl_bo_unref(&bo);
250	return ret;
251}
252
253int qxl_fence_releaseable(struct qxl_device *qdev,
254			  struct qxl_release *release)
255{
256	int i, ret;
257	for (i = 0; i < release->bo_count; i++) {
258		if (!release->bos[i]->tbo.sync_obj)
259			release->bos[i]->tbo.sync_obj = &release->bos[i]->fence;
260		ret = qxl_fence_add_release(&release->bos[i]->fence, release->id);
261		if (ret)
262			return ret;
263	}
264	return 0;
265}
266
267struct qxl_release *qxl_release_from_id_locked(struct qxl_device *qdev,
268						   uint64_t id)
269{
270	struct qxl_release *release;
271
272	spin_lock(&qdev->release_idr_lock);
273	release = idr_find(&qdev->release_idr, id);
274	spin_unlock(&qdev->release_idr_lock);
275	if (!release) {
276		DRM_ERROR("failed to find id in release_idr\n");
277		return NULL;
278	}
279	if (release->bo_count < 1) {
280		DRM_ERROR("read a released resource with 0 bos\n");
281		return NULL;
282	}
283	return release;
284}
285
286union qxl_release_info *qxl_release_map(struct qxl_device *qdev,
287					struct qxl_release *release)
288{
289	void *ptr;
290	union qxl_release_info *info;
291	struct qxl_bo *bo = release->bos[0];
292
293	ptr = qxl_bo_kmap_atomic_page(qdev, bo, release->release_offset & PAGE_SIZE);
294	info = ptr + (release->release_offset & ~PAGE_SIZE);
295	return info;
296}
297
298void qxl_release_unmap(struct qxl_device *qdev,
299		       struct qxl_release *release,
300		       union qxl_release_info *info)
301{
302	struct qxl_bo *bo = release->bos[0];
303	void *ptr;
304
305	ptr = ((void *)info) - (release->release_offset & ~PAGE_SIZE);
306	qxl_bo_kunmap_atomic_page(qdev, bo, ptr);
307}
308