aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/i915/i915_gem.c
diff options
context:
space:
mode:
authorEric Anholt <eric@anholt.net>2009-03-09 16:42:30 -0400
committerEric Anholt <eric@anholt.net>2009-03-27 17:47:13 -0400
commit40123c1f8dd920dcff7a42cde5b351d7d0b0422e (patch)
treee088817fd6239fe280d53fdb1907864bdf69ca7e /drivers/gpu/drm/i915/i915_gem.c
parent856fa1988ea483fc2dab84a16681dcfde821b740 (diff)
drm/i915: Fix lock order reversal in shmem pwrite path.
Like the GTT pwrite path fix, this uses an optimistic path and a fallback to get_user_pages. Note that this means we have to stop using vfs_write and roll it ourselves. Signed-off-by: Eric Anholt <eric@anholt.net> Reviewed-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Diffstat (limited to 'drivers/gpu/drm/i915/i915_gem.c')
-rw-r--r--drivers/gpu/drm/i915/i915_gem.c225
1 files changed, 205 insertions, 20 deletions
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index b998d659fd98..bdc7326052df 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -136,6 +136,33 @@ i915_gem_create_ioctl(struct drm_device *dev, void *data,
136 return 0; 136 return 0;
137} 137}
138 138
139static inline int
140slow_shmem_copy(struct page *dst_page,
141 int dst_offset,
142 struct page *src_page,
143 int src_offset,
144 int length)
145{
146 char *dst_vaddr, *src_vaddr;
147
148 dst_vaddr = kmap_atomic(dst_page, KM_USER0);
149 if (dst_vaddr == NULL)
150 return -ENOMEM;
151
152 src_vaddr = kmap_atomic(src_page, KM_USER1);
153 if (src_vaddr == NULL) {
154 kunmap_atomic(dst_vaddr, KM_USER0);
155 return -ENOMEM;
156 }
157
158 memcpy(dst_vaddr + dst_offset, src_vaddr + src_offset, length);
159
160 kunmap_atomic(src_vaddr, KM_USER1);
161 kunmap_atomic(dst_vaddr, KM_USER0);
162
163 return 0;
164}
165
139/** 166/**
140 * Reads data from the object referenced by handle. 167 * Reads data from the object referenced by handle.
141 * 168 *
@@ -243,6 +270,23 @@ slow_kernel_write(struct io_mapping *mapping,
243 return 0; 270 return 0;
244} 271}
245 272
273static inline int
274fast_shmem_write(struct page **pages,
275 loff_t page_base, int page_offset,
276 char __user *data,
277 int length)
278{
279 char __iomem *vaddr;
280
281 vaddr = kmap_atomic(pages[page_base >> PAGE_SHIFT], KM_USER0);
282 if (vaddr == NULL)
283 return -ENOMEM;
284 __copy_from_user_inatomic(vaddr + page_offset, data, length);
285 kunmap_atomic(vaddr, KM_USER0);
286
287 return 0;
288}
289
246/** 290/**
247 * This is the fast pwrite path, where we copy the data directly from the 291 * This is the fast pwrite path, where we copy the data directly from the
248 * user into the GTT, uncached. 292 * user into the GTT, uncached.
@@ -423,39 +467,175 @@ out_unpin_pages:
423 return ret; 467 return ret;
424} 468}
425 469
470/**
471 * This is the fast shmem pwrite path, which attempts to directly
472 * copy_from_user into the kmapped pages backing the object.
473 */
426static int 474static int
427i915_gem_shmem_pwrite(struct drm_device *dev, struct drm_gem_object *obj, 475i915_gem_shmem_pwrite_fast(struct drm_device *dev, struct drm_gem_object *obj,
428 struct drm_i915_gem_pwrite *args, 476 struct drm_i915_gem_pwrite *args,
429 struct drm_file *file_priv) 477 struct drm_file *file_priv)
430{ 478{
479 struct drm_i915_gem_object *obj_priv = obj->driver_private;
480 ssize_t remain;
481 loff_t offset, page_base;
482 char __user *user_data;
483 int page_offset, page_length;
431 int ret; 484 int ret;
432 loff_t offset; 485
433 ssize_t written; 486 user_data = (char __user *) (uintptr_t) args->data_ptr;
487 remain = args->size;
434 488
435 mutex_lock(&dev->struct_mutex); 489 mutex_lock(&dev->struct_mutex);
436 490
491 ret = i915_gem_object_get_pages(obj);
492 if (ret != 0)
493 goto fail_unlock;
494
437 ret = i915_gem_object_set_to_cpu_domain(obj, 1); 495 ret = i915_gem_object_set_to_cpu_domain(obj, 1);
438 if (ret) { 496 if (ret != 0)
439 mutex_unlock(&dev->struct_mutex); 497 goto fail_put_pages;
440 return ret; 498
499 obj_priv = obj->driver_private;
500 offset = args->offset;
501 obj_priv->dirty = 1;
502
503 while (remain > 0) {
504 /* Operation in this page
505 *
506 * page_base = page offset within aperture
507 * page_offset = offset within page
508 * page_length = bytes to copy for this page
509 */
510 page_base = (offset & ~(PAGE_SIZE-1));
511 page_offset = offset & (PAGE_SIZE-1);
512 page_length = remain;
513 if ((page_offset + remain) > PAGE_SIZE)
514 page_length = PAGE_SIZE - page_offset;
515
516 ret = fast_shmem_write(obj_priv->pages,
517 page_base, page_offset,
518 user_data, page_length);
519 if (ret)
520 goto fail_put_pages;
521
522 remain -= page_length;
523 user_data += page_length;
524 offset += page_length;
441 } 525 }
442 526
527fail_put_pages:
528 i915_gem_object_put_pages(obj);
529fail_unlock:
530 mutex_unlock(&dev->struct_mutex);
531
532 return ret;
533}
534
535/**
536 * This is the fallback shmem pwrite path, which uses get_user_pages to pin
537 * the memory and maps it using kmap_atomic for copying.
538 *
539 * This avoids taking mmap_sem for faulting on the user's address while the
540 * struct_mutex is held.
541 */
542static int
543i915_gem_shmem_pwrite_slow(struct drm_device *dev, struct drm_gem_object *obj,
544 struct drm_i915_gem_pwrite *args,
545 struct drm_file *file_priv)
546{
547 struct drm_i915_gem_object *obj_priv = obj->driver_private;
548 struct mm_struct *mm = current->mm;
549 struct page **user_pages;
550 ssize_t remain;
551 loff_t offset, pinned_pages, i;
552 loff_t first_data_page, last_data_page, num_pages;
553 int shmem_page_index, shmem_page_offset;
554 int data_page_index, data_page_offset;
555 int page_length;
556 int ret;
557 uint64_t data_ptr = args->data_ptr;
558
559 remain = args->size;
560
561 /* Pin the user pages containing the data. We can't fault while
562 * holding the struct mutex, and all of the pwrite implementations
563 * want to hold it while dereferencing the user data.
564 */
565 first_data_page = data_ptr / PAGE_SIZE;
566 last_data_page = (data_ptr + args->size - 1) / PAGE_SIZE;
567 num_pages = last_data_page - first_data_page + 1;
568
569 user_pages = kcalloc(num_pages, sizeof(struct page *), GFP_KERNEL);
570 if (user_pages == NULL)
571 return -ENOMEM;
572
573 down_read(&mm->mmap_sem);
574 pinned_pages = get_user_pages(current, mm, (uintptr_t)args->data_ptr,
575 num_pages, 0, 0, user_pages, NULL);
576 up_read(&mm->mmap_sem);
577 if (pinned_pages < num_pages) {
578 ret = -EFAULT;
579 goto fail_put_user_pages;
580 }
581
582 mutex_lock(&dev->struct_mutex);
583
584 ret = i915_gem_object_get_pages(obj);
585 if (ret != 0)
586 goto fail_unlock;
587
588 ret = i915_gem_object_set_to_cpu_domain(obj, 1);
589 if (ret != 0)
590 goto fail_put_pages;
591
592 obj_priv = obj->driver_private;
443 offset = args->offset; 593 offset = args->offset;
594 obj_priv->dirty = 1;
444 595
445 written = vfs_write(obj->filp, 596 while (remain > 0) {
446 (char __user *)(uintptr_t) args->data_ptr, 597 /* Operation in this page
447 args->size, &offset); 598 *
448 if (written != args->size) { 599 * shmem_page_index = page number within shmem file
449 mutex_unlock(&dev->struct_mutex); 600 * shmem_page_offset = offset within page in shmem file
450 if (written < 0) 601 * data_page_index = page number in get_user_pages return
451 return written; 602 * data_page_offset = offset with data_page_index page.
452 else 603 * page_length = bytes to copy for this page
453 return -EINVAL; 604 */
605 shmem_page_index = offset / PAGE_SIZE;
606 shmem_page_offset = offset & ~PAGE_MASK;
607 data_page_index = data_ptr / PAGE_SIZE - first_data_page;
608 data_page_offset = data_ptr & ~PAGE_MASK;
609
610 page_length = remain;
611 if ((shmem_page_offset + page_length) > PAGE_SIZE)
612 page_length = PAGE_SIZE - shmem_page_offset;
613 if ((data_page_offset + page_length) > PAGE_SIZE)
614 page_length = PAGE_SIZE - data_page_offset;
615
616 ret = slow_shmem_copy(obj_priv->pages[shmem_page_index],
617 shmem_page_offset,
618 user_pages[data_page_index],
619 data_page_offset,
620 page_length);
621 if (ret)
622 goto fail_put_pages;
623
624 remain -= page_length;
625 data_ptr += page_length;
626 offset += page_length;
454 } 627 }
455 628
629fail_put_pages:
630 i915_gem_object_put_pages(obj);
631fail_unlock:
456 mutex_unlock(&dev->struct_mutex); 632 mutex_unlock(&dev->struct_mutex);
633fail_put_user_pages:
634 for (i = 0; i < pinned_pages; i++)
635 page_cache_release(user_pages[i]);
636 kfree(user_pages);
457 637
458 return 0; 638 return ret;
459} 639}
460 640
461/** 641/**
@@ -502,8 +682,13 @@ i915_gem_pwrite_ioctl(struct drm_device *dev, void *data,
502 ret = i915_gem_gtt_pwrite_slow(dev, obj, args, 682 ret = i915_gem_gtt_pwrite_slow(dev, obj, args,
503 file_priv); 683 file_priv);
504 } 684 }
505 } else 685 } else {
506 ret = i915_gem_shmem_pwrite(dev, obj, args, file_priv); 686 ret = i915_gem_shmem_pwrite_fast(dev, obj, args, file_priv);
687 if (ret == -EFAULT) {
688 ret = i915_gem_shmem_pwrite_slow(dev, obj, args,
689 file_priv);
690 }
691 }
507 692
508#if WATCH_PWRITE 693#if WATCH_PWRITE
509 if (ret) 694 if (ret)