Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/vmwgfx: Implement an infrastructure for read-coherent resources

Similar to write-coherent resources, make sure that from the user-space
point of view, GPU rendered contents is automatically available for
reading by the CPU.

Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Matthew Wilcox <willy@infradead.org>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Rik van Riel <riel@surriel.com>
Cc: Minchan Kim <minchan@kernel.org>
Cc: Michal Hocko <mhocko@suse.com>
Cc: Huang Ying <ying.huang@intel.com>
Cc: Jérôme Glisse <jglisse@redhat.com>
Cc: Kirill A. Shutemov <kirill@shutemov.name>
Signed-off-by: Thomas Hellstrom <thellstrom@vmware.com>
Reviewed-by: Deepak Rawat <drawat@vmware.com>

+181 -11
+6 -1
drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
··· 684 684 extern struct vmw_resource *vmw_resource_reference(struct vmw_resource *res); 685 685 extern struct vmw_resource * 686 686 vmw_resource_reference_unless_doomed(struct vmw_resource *res); 687 - extern int vmw_resource_validate(struct vmw_resource *res, bool intr); 687 + extern int vmw_resource_validate(struct vmw_resource *res, bool intr, 688 + bool dirtying); 688 689 extern int vmw_resource_reserve(struct vmw_resource *res, bool interruptible, 689 690 bool no_backup); 690 691 extern bool vmw_resource_needs_backup(const struct vmw_resource *res); ··· 729 728 void vmw_resource_mob_detach(struct vmw_resource *res); 730 729 void vmw_resource_dirty_update(struct vmw_resource *res, pgoff_t start, 731 730 pgoff_t end); 731 + int vmw_resources_clean(struct vmw_buffer_object *vbo, pgoff_t start, 732 + pgoff_t end, pgoff_t *num_prefault); 732 733 733 734 /** 734 735 * vmw_resource_mob_attached - Whether a resource currently has a mob attached ··· 1424 1421 void vmw_bo_dirty_transfer_to_res(struct vmw_resource *res); 1425 1422 void vmw_bo_dirty_clear_res(struct vmw_resource *res); 1426 1423 void vmw_bo_dirty_release(struct vmw_buffer_object *vbo); 1424 + void vmw_bo_dirty_unmap(struct vmw_buffer_object *vbo, 1425 + pgoff_t start, pgoff_t end); 1427 1426 vm_fault_t vmw_bo_vm_fault(struct vm_fault *vmf); 1428 1427 vm_fault_t vmw_bo_vm_mkwrite(struct vm_fault *vmf); 1429 1428
+72 -5
drivers/gpu/drm/vmwgfx/vmwgfx_page_dirty.c
··· 155 155 } 156 156 } 157 157 158 - 159 158 /** 160 159 * vmw_bo_dirty_scan - Scan for dirty pages and add them to the dirty 161 160 * tracking structure ··· 170 171 vmw_bo_dirty_scan_pagetable(vbo); 171 172 else 172 173 vmw_bo_dirty_scan_mkwrite(vbo); 174 + } 175 + 176 + /** 177 + * vmw_bo_dirty_pre_unmap - write-protect and pick up dirty pages before 178 + * an unmap_mapping_range operation. 179 + * @vbo: The buffer object, 180 + * @start: First page of the range within the buffer object. 181 + * @end: Last page of the range within the buffer object + 1. 182 + * 183 + * If we're using the _PAGETABLE scan method, we may leak dirty pages 184 + * when calling unmap_mapping_range(). This function makes sure we pick 185 + * up all dirty pages. 186 + */ 187 + static void vmw_bo_dirty_pre_unmap(struct vmw_buffer_object *vbo, 188 + pgoff_t start, pgoff_t end) 189 + { 190 + struct vmw_bo_dirty *dirty = vbo->dirty; 191 + unsigned long offset = drm_vma_node_start(&vbo->base.base.vma_node); 192 + struct address_space *mapping = vbo->base.bdev->dev_mapping; 193 + 194 + if (dirty->method != VMW_BO_DIRTY_PAGETABLE || start >= end) 195 + return; 196 + 197 + wp_shared_mapping_range(mapping, start + offset, end - start); 198 + clean_record_shared_mapping_range(mapping, start + offset, 199 + end - start, offset, 200 + &dirty->bitmap[0], &dirty->start, 201 + &dirty->end); 202 + } 203 + 204 + /** 205 + * vmw_bo_dirty_unmap - Clear all ptes pointing to a range within a bo 206 + * @vbo: The buffer object, 207 + * @start: First page of the range within the buffer object. 208 + * @end: Last page of the range within the buffer object + 1. 209 + * 210 + * This is similar to ttm_bo_unmap_virtual_locked() except it takes a subrange. 211 + */ 212 + void vmw_bo_dirty_unmap(struct vmw_buffer_object *vbo, 213 + pgoff_t start, pgoff_t end) 214 + { 215 + unsigned long offset = drm_vma_node_start(&vbo->base.base.vma_node); 216 + struct address_space *mapping = vbo->base.bdev->dev_mapping; 217 + 218 + vmw_bo_dirty_pre_unmap(vbo, start, end); 219 + unmap_shared_mapping_range(mapping, (offset + start) << PAGE_SHIFT, 220 + (loff_t) (end - start) << PAGE_SHIFT); 173 221 } 174 222 175 223 /** ··· 447 401 if (ret) 448 402 return ret; 449 403 404 + num_prefault = (vma->vm_flags & VM_RAND_READ) ? 1 : 405 + TTM_BO_VM_NUM_PREFAULT; 406 + 407 + if (vbo->dirty) { 408 + pgoff_t allowed_prefault; 409 + unsigned long page_offset; 410 + 411 + page_offset = vmf->pgoff - 412 + drm_vma_node_start(&bo->base.vma_node); 413 + if (page_offset >= bo->num_pages || 414 + vmw_resources_clean(vbo, page_offset, 415 + page_offset + PAGE_SIZE, 416 + &allowed_prefault)) { 417 + ret = VM_FAULT_SIGBUS; 418 + goto out_unlock; 419 + } 420 + 421 + num_prefault = min(num_prefault, allowed_prefault); 422 + } 423 + 450 424 /* 451 - * This will cause mkwrite() to be called for each pte on 452 - * write-enable vmas. 425 + * If we don't track dirty using the MKWRITE method, make sure 426 + * sure the page protection is write-enabled so we don't get 427 + * a lot of unnecessary write faults. 453 428 */ 454 429 if (vbo->dirty && vbo->dirty->method == VMW_BO_DIRTY_MKWRITE) 455 430 prot = vma->vm_page_prot; 456 431 else 457 432 prot = vm_get_page_prot(vma->vm_flags); 458 433 459 - num_prefault = (vma->vm_flags & VM_RAND_READ) ? 0 : 460 - TTM_BO_VM_NUM_PREFAULT; 461 434 ret = ttm_bo_vm_fault_reserved(vmf, prot, num_prefault); 462 435 if (ret == VM_FAULT_RETRY && !(vmf->flags & FAULT_FLAG_RETRY_NOWAIT)) 463 436 return ret; 464 437 438 + out_unlock: 465 439 dma_resv_unlock(bo->base.resv); 440 + 466 441 return ret; 467 442 }
+99 -4
drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
··· 393 393 * should be retried once resources have been freed up. 394 394 */ 395 395 static int vmw_resource_do_validate(struct vmw_resource *res, 396 - struct ttm_validate_buffer *val_buf) 396 + struct ttm_validate_buffer *val_buf, 397 + bool dirtying) 397 398 { 398 399 int ret = 0; 399 400 const struct vmw_res_func *func = res->func; ··· 436 435 * the resource. 437 436 */ 438 437 if (res->dirty) { 438 + if (dirtying && !res->res_dirty) { 439 + pgoff_t start = res->backup_offset >> PAGE_SHIFT; 440 + pgoff_t end = __KERNEL_DIV_ROUND_UP 441 + (res->backup_offset + res->backup_size, 442 + PAGE_SIZE); 443 + 444 + vmw_bo_dirty_unmap(res->backup, start, end); 445 + } 446 + 439 447 vmw_bo_dirty_transfer_to_res(res); 440 448 return func->dirty_sync(res); 441 449 } ··· 688 678 * to the device. 689 679 * @res: The resource to make visible to the device. 690 680 * @intr: Perform waits interruptible if possible. 681 + * @dirtying: Pending GPU operation will dirty the resource 691 682 * 692 683 * On succesful return, any backup DMA buffer pointed to by @res->backup will 693 684 * be reserved and validated. ··· 698 687 * Return: Zero on success, -ERESTARTSYS if interrupted, negative error code 699 688 * on failure. 700 689 */ 701 - int vmw_resource_validate(struct vmw_resource *res, bool intr) 690 + int vmw_resource_validate(struct vmw_resource *res, bool intr, 691 + bool dirtying) 702 692 { 703 693 int ret; 704 694 struct vmw_resource *evict_res; ··· 716 704 if (res->backup) 717 705 val_buf.bo = &res->backup->base; 718 706 do { 719 - ret = vmw_resource_do_validate(res, &val_buf); 707 + ret = vmw_resource_do_validate(res, &val_buf, dirtying); 720 708 if (likely(ret != -EBUSY)) 721 709 break; 722 710 ··· 1016 1004 /* Do we really need to pin the MOB as well? */ 1017 1005 vmw_bo_pin_reserved(vbo, true); 1018 1006 } 1019 - ret = vmw_resource_validate(res, interruptible); 1007 + ret = vmw_resource_validate(res, interruptible, true); 1020 1008 if (vbo) 1021 1009 ttm_bo_unreserve(&vbo->base); 1022 1010 if (ret) ··· 1090 1078 if (res->dirty) 1091 1079 res->func->dirty_range_add(res, start << PAGE_SHIFT, 1092 1080 end << PAGE_SHIFT); 1081 + } 1082 + 1083 + /** 1084 + * vmw_resources_clean - Clean resources intersecting a mob range 1085 + * @vbo: The mob buffer object 1086 + * @start: The mob page offset starting the range 1087 + * @end: The mob page offset ending the range 1088 + * @num_prefault: Returns how many pages including the first have been 1089 + * cleaned and are ok to prefault 1090 + */ 1091 + int vmw_resources_clean(struct vmw_buffer_object *vbo, pgoff_t start, 1092 + pgoff_t end, pgoff_t *num_prefault) 1093 + { 1094 + struct rb_node *cur = vbo->res_tree.rb_node; 1095 + struct vmw_resource *found = NULL; 1096 + unsigned long res_start = start << PAGE_SHIFT; 1097 + unsigned long res_end = end << PAGE_SHIFT; 1098 + unsigned long last_cleaned = 0; 1099 + 1100 + /* 1101 + * Find the resource with lowest backup_offset that intersects the 1102 + * range. 1103 + */ 1104 + while (cur) { 1105 + struct vmw_resource *cur_res = 1106 + container_of(cur, struct vmw_resource, mob_node); 1107 + 1108 + if (cur_res->backup_offset >= res_end) { 1109 + cur = cur->rb_left; 1110 + } else if (cur_res->backup_offset + cur_res->backup_size <= 1111 + res_start) { 1112 + cur = cur->rb_right; 1113 + } else { 1114 + found = cur_res; 1115 + cur = cur->rb_left; 1116 + /* Continue to look for resources with lower offsets */ 1117 + } 1118 + } 1119 + 1120 + /* 1121 + * In order of increasing backup_offset, clean dirty resorces 1122 + * intersecting the range. 1123 + */ 1124 + while (found) { 1125 + if (found->res_dirty) { 1126 + int ret; 1127 + 1128 + if (!found->func->clean) 1129 + return -EINVAL; 1130 + 1131 + ret = found->func->clean(found); 1132 + if (ret) 1133 + return ret; 1134 + 1135 + found->res_dirty = false; 1136 + } 1137 + last_cleaned = found->backup_offset + found->backup_size; 1138 + cur = rb_next(&found->mob_node); 1139 + if (!cur) 1140 + break; 1141 + 1142 + found = container_of(cur, struct vmw_resource, mob_node); 1143 + if (found->backup_offset >= res_end) 1144 + break; 1145 + } 1146 + 1147 + /* 1148 + * Set number of pages allowed prefaulting and fence the buffer object 1149 + */ 1150 + *num_prefault = 1; 1151 + if (last_cleaned > res_start) { 1152 + struct ttm_buffer_object *bo = &vbo->base; 1153 + 1154 + *num_prefault = __KERNEL_DIV_ROUND_UP(last_cleaned - res_start, 1155 + PAGE_SIZE); 1156 + vmw_bo_fence_single(bo, NULL); 1157 + if (bo->moving) 1158 + dma_fence_put(bo->moving); 1159 + bo->moving = dma_fence_get 1160 + (dma_resv_get_excl(bo->base.resv)); 1161 + } 1162 + 1163 + return 0; 1093 1164 }
+2
drivers/gpu/drm/vmwgfx/vmwgfx_resource_priv.h
··· 77 77 * @dirty_sync: Upload the dirty mob contents to the resource. 78 78 * @dirty_add_range: Add a sequential dirty range to the resource 79 79 * dirty tracker. 80 + * @clean: Clean the resource. 80 81 */ 81 82 struct vmw_res_func { 82 83 enum vmw_res_type res_type; ··· 102 101 int (*dirty_sync)(struct vmw_resource *res); 103 102 void (*dirty_range_add)(struct vmw_resource *res, size_t start, 104 103 size_t end); 104 + int (*clean)(struct vmw_resource *res); 105 105 }; 106 106 107 107 /**
+2 -1
drivers/gpu/drm/vmwgfx/vmwgfx_validation.c
··· 644 644 struct vmw_resource *res = val->res; 645 645 struct vmw_buffer_object *backup = res->backup; 646 646 647 - ret = vmw_resource_validate(res, intr); 647 + ret = vmw_resource_validate(res, intr, val->dirty_set && 648 + val->dirty); 648 649 if (ret) { 649 650 if (ret != -ERESTARTSYS) 650 651 DRM_ERROR("Failed to validate resource.\n");