aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/radeon/radeon_cs.c
diff options
context:
space:
mode:
authorDave Airlie <airlied@redhat.com>2009-09-23 02:56:27 -0400
committerDave Airlie <airlied@linux.ie>2009-09-24 23:08:18 -0400
commit513bcb4655e68706594e45dfa1d4b181500110ba (patch)
treeed457db4cfb202015866a131ad4e742503728fad /drivers/gpu/drm/radeon/radeon_cs.c
parent35e4b7af21d77933abda3d41d1672589eb6c960c (diff)
drm/radeon/kms: don't require up to 64k allocations. (v2)
This avoids needing to do a kmalloc > PAGE_SIZE for the main indirect buffer chunk, it adds an accessor for all reads from the chunk and caches a single page at a time for subsequent reads. changes since v1: Use a two page pool which should be the most common case a single packet spanning > PAGE_SIZE will be hit, but I'm having trouble seeing anywhere we currently generate anything like that. hopefully proper short page copying at end added parser_error flag to set deep errors instead of having to test every ib value fetch. fixed bug in patch that went to list. Signed-off-by: Dave Airlie <airlied@redhat.com>
Diffstat (limited to 'drivers/gpu/drm/radeon/radeon_cs.c')
-rw-r--r--drivers/gpu/drm/radeon/radeon_cs.c105
1 files changed, 96 insertions, 9 deletions
diff --git a/drivers/gpu/drm/radeon/radeon_cs.c b/drivers/gpu/drm/radeon/radeon_cs.c
index 12f5990c2d2a..dea8acf88865 100644
--- a/drivers/gpu/drm/radeon/radeon_cs.c
+++ b/drivers/gpu/drm/radeon/radeon_cs.c
@@ -142,15 +142,31 @@ int radeon_cs_parser_init(struct radeon_cs_parser *p, void *data)
142 } 142 }
143 143
144 p->chunks[i].length_dw = user_chunk.length_dw; 144 p->chunks[i].length_dw = user_chunk.length_dw;
145 cdata = (uint32_t *)(unsigned long)user_chunk.chunk_data; 145 p->chunks[i].user_ptr = (void __user *)(unsigned long)user_chunk.chunk_data;
146 146
147 size = p->chunks[i].length_dw * sizeof(uint32_t); 147 cdata = (uint32_t *)(unsigned long)user_chunk.chunk_data;
148 p->chunks[i].kdata = kmalloc(size, GFP_KERNEL); 148 if (p->chunks[i].chunk_id != RADEON_CHUNK_ID_IB) {
149 if (p->chunks[i].kdata == NULL) { 149 size = p->chunks[i].length_dw * sizeof(uint32_t);
150 return -ENOMEM; 150 p->chunks[i].kdata = kmalloc(size, GFP_KERNEL);
151 } 151 if (p->chunks[i].kdata == NULL) {
152 if (DRM_COPY_FROM_USER(p->chunks[i].kdata, cdata, size)) { 152 return -ENOMEM;
153 return -EFAULT; 153 }
154 if (DRM_COPY_FROM_USER(p->chunks[i].kdata,
155 p->chunks[i].user_ptr, size)) {
156 return -EFAULT;
157 }
158 } else {
159 p->chunks[i].kpage[0] = kmalloc(PAGE_SIZE, GFP_KERNEL);
160 p->chunks[i].kpage[1] = kmalloc(PAGE_SIZE, GFP_KERNEL);
161 if (p->chunks[i].kpage[0] == NULL || p->chunks[i].kpage[1] == NULL) {
162 kfree(p->chunks[i].kpage[0]);
163 kfree(p->chunks[i].kpage[1]);
164 return -ENOMEM;
165 }
166 p->chunks[i].kpage_idx[0] = -1;
167 p->chunks[i].kpage_idx[1] = -1;
168 p->chunks[i].last_copied_page = -1;
169 p->chunks[i].last_page_index = ((p->chunks[i].length_dw * 4) - 1) / PAGE_SIZE;
154 } 170 }
155 } 171 }
156 if (p->chunks[p->chunk_ib_idx].length_dw > (16 * 1024)) { 172 if (p->chunks[p->chunk_ib_idx].length_dw > (16 * 1024)) {
@@ -190,6 +206,8 @@ static void radeon_cs_parser_fini(struct radeon_cs_parser *parser, int error)
190 kfree(parser->relocs_ptr); 206 kfree(parser->relocs_ptr);
191 for (i = 0; i < parser->nchunks; i++) { 207 for (i = 0; i < parser->nchunks; i++) {
192 kfree(parser->chunks[i].kdata); 208 kfree(parser->chunks[i].kdata);
209 kfree(parser->chunks[i].kpage[0]);
210 kfree(parser->chunks[i].kpage[1]);
193 } 211 }
194 kfree(parser->chunks); 212 kfree(parser->chunks);
195 kfree(parser->chunks_array); 213 kfree(parser->chunks_array);
@@ -238,8 +256,14 @@ int radeon_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
238 * uncached). */ 256 * uncached). */
239 ib_chunk = &parser.chunks[parser.chunk_ib_idx]; 257 ib_chunk = &parser.chunks[parser.chunk_ib_idx];
240 parser.ib->length_dw = ib_chunk->length_dw; 258 parser.ib->length_dw = ib_chunk->length_dw;
241 memcpy((void *)parser.ib->ptr, ib_chunk->kdata, ib_chunk->length_dw*4);
242 r = radeon_cs_parse(&parser); 259 r = radeon_cs_parse(&parser);
260 if (r || parser.parser_error) {
261 DRM_ERROR("Invalid command stream !\n");
262 radeon_cs_parser_fini(&parser, r);
263 mutex_unlock(&rdev->cs_mutex);
264 return r;
265 }
266 r = radeon_cs_finish_pages(&parser);
243 if (r) { 267 if (r) {
244 DRM_ERROR("Invalid command stream !\n"); 268 DRM_ERROR("Invalid command stream !\n");
245 radeon_cs_parser_fini(&parser, r); 269 radeon_cs_parser_fini(&parser, r);
@@ -254,3 +278,66 @@ int radeon_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
254 mutex_unlock(&rdev->cs_mutex); 278 mutex_unlock(&rdev->cs_mutex);
255 return r; 279 return r;
256} 280}
281
282int radeon_cs_finish_pages(struct radeon_cs_parser *p)
283{
284 struct radeon_cs_chunk *ibc = &p->chunks[p->chunk_ib_idx];
285 int i;
286 int size = PAGE_SIZE;
287
288 for (i = ibc->last_copied_page + 1; i <= ibc->last_page_index; i++) {
289 if (i == ibc->last_page_index) {
290 size = (ibc->length_dw * 4) % PAGE_SIZE;
291 if (size == 0)
292 size = PAGE_SIZE;
293 }
294
295 if (DRM_COPY_FROM_USER(p->ib->ptr + (i * (PAGE_SIZE/4)),
296 ibc->user_ptr + (i * PAGE_SIZE),
297 size))
298 return -EFAULT;
299 }
300 return 0;
301}
302
303int radeon_cs_update_pages(struct radeon_cs_parser *p, int pg_idx)
304{
305 int new_page;
306 int num_extra_pages;
307 struct radeon_cs_chunk *ibc = &p->chunks[p->chunk_ib_idx];
308 int i;
309 int size = PAGE_SIZE;
310
311 num_extra_pages = (pg_idx - ibc->last_copied_page - 1);
312 for (i = ibc->last_copied_page + 1; i < ibc->last_copied_page + num_extra_pages; i++) {
313 if (DRM_COPY_FROM_USER(p->ib->ptr + (i * (PAGE_SIZE/4)),
314 ibc->user_ptr + (i * PAGE_SIZE),
315 PAGE_SIZE)) {
316 p->parser_error = -EFAULT;
317 return 0;
318 }
319 }
320
321 new_page = ibc->kpage_idx[0] < ibc->kpage_idx[1] ? 0 : 1;
322
323 if (pg_idx == ibc->last_page_index) {
324 size = (ibc->length_dw * 4) % PAGE_SIZE;
325 if (size == 0)
326 size = PAGE_SIZE;
327 }
328
329 if (DRM_COPY_FROM_USER(ibc->kpage[new_page],
330 ibc->user_ptr + (pg_idx * PAGE_SIZE),
331 size)) {
332 p->parser_error = -EFAULT;
333 return 0;
334 }
335
336 /* copy to IB here */
337 memcpy((void *)(p->ib->ptr+(pg_idx*(PAGE_SIZE/4))), ibc->kpage[new_page], size);
338
339 ibc->last_copied_page = pg_idx;
340 ibc->kpage_idx[new_page] = pg_idx;
341
342 return new_page;
343}