diff options
Diffstat (limited to 'drivers/gpu/drm/ttm')
| -rw-r--r-- | drivers/gpu/drm/ttm/Makefile | 6 | ||||
| -rw-r--r-- | drivers/gpu/drm/ttm/ttm_agp_backend.c | 1 | ||||
| -rw-r--r-- | drivers/gpu/drm/ttm/ttm_bo.c | 116 | ||||
| -rw-r--r-- | drivers/gpu/drm/ttm/ttm_bo_manager.c | 11 | ||||
| -rw-r--r-- | drivers/gpu/drm/ttm/ttm_bo_util.c | 42 | ||||
| -rw-r--r-- | drivers/gpu/drm/ttm/ttm_bo_vm.c | 156 | ||||
| -rw-r--r-- | drivers/gpu/drm/ttm/ttm_execbuf_util.c | 36 | ||||
| -rw-r--r-- | drivers/gpu/drm/ttm/ttm_lock.c | 8 | ||||
| -rw-r--r-- | drivers/gpu/drm/ttm/ttm_object.c | 390 | ||||
| -rw-r--r-- | drivers/gpu/drm/ttm/ttm_page_alloc_dma.c | 3 | ||||
| -rw-r--r-- | drivers/gpu/drm/ttm/ttm_tt.c | 30 | 
11 files changed, 637 insertions, 162 deletions
diff --git a/drivers/gpu/drm/ttm/Makefile b/drivers/gpu/drm/ttm/Makefile index b2b33dde2af..b433b9f040c 100644 --- a/drivers/gpu/drm/ttm/Makefile +++ b/drivers/gpu/drm/ttm/Makefile @@ -5,10 +5,6 @@ ccflags-y := -Iinclude/drm  ttm-y := ttm_agp_backend.o ttm_memory.o ttm_tt.o ttm_bo.o \  	ttm_bo_util.o ttm_bo_vm.o ttm_module.o \  	ttm_object.o ttm_lock.o ttm_execbuf_util.o ttm_page_alloc.o \ -	ttm_bo_manager.o - -ifeq ($(CONFIG_SWIOTLB),y) -ttm-y += ttm_page_alloc_dma.o -endif +	ttm_bo_manager.o ttm_page_alloc_dma.o  obj-$(CONFIG_DRM_TTM) += ttm.o diff --git a/drivers/gpu/drm/ttm/ttm_agp_backend.c b/drivers/gpu/drm/ttm/ttm_agp_backend.c index 3302f99e749..764be36397f 100644 --- a/drivers/gpu/drm/ttm/ttm_agp_backend.c +++ b/drivers/gpu/drm/ttm/ttm_agp_backend.c @@ -126,6 +126,7 @@ struct ttm_tt *ttm_agp_tt_create(struct ttm_bo_device *bdev,  	agp_be->ttm.func = &ttm_agp_func;  	if (ttm_tt_init(&agp_be->ttm, bdev, size, page_flags, dummy_read_page)) { +		kfree(agp_be);  		return NULL;  	} diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index f1a857ec102..4ab9f7171c4 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -151,7 +151,7 @@ static void ttm_bo_release_list(struct kref *list_kref)  	atomic_dec(&bo->glob->bo_count);  	if (bo->resv == &bo->ttm_resv)  		reservation_object_fini(&bo->ttm_resv); - +	mutex_destroy(&bo->wu_mutex);  	if (bo->destroy)  		bo->destroy(bo);  	else { @@ -351,9 +351,11 @@ static int ttm_bo_handle_move_mem(struct ttm_buffer_object *bo,  moved:  	if (bo->evicted) { -		ret = bdev->driver->invalidate_caches(bdev, bo->mem.placement); -		if (ret) -			pr_err("Can not flush read caches\n"); +		if (bdev->driver->invalidate_caches) { +			ret = bdev->driver->invalidate_caches(bdev, bo->mem.placement); +			if (ret) +				pr_err("Can not flush read caches\n"); +		}  		bo->evicted = false;  	} @@ -410,7 +412,7 @@ static void ttm_bo_cleanup_refs_or_queue(struct ttm_buffer_object *bo)  	int ret;  	spin_lock(&glob->lru_lock); -	ret = ttm_bo_reserve_nolru(bo, false, true, false, 0); +	ret = __ttm_bo_reserve(bo, false, true, false, 0);  	spin_lock(&bdev->fence_lock);  	(void) ttm_bo_wait(bo, false, false, true); @@ -429,8 +431,20 @@ static void ttm_bo_cleanup_refs_or_queue(struct ttm_buffer_object *bo)  		sync_obj = driver->sync_obj_ref(bo->sync_obj);  	spin_unlock(&bdev->fence_lock); -	if (!ret) -		ww_mutex_unlock(&bo->resv->lock); +	if (!ret) { + +		/* +		 * Make NO_EVICT bos immediately available to +		 * shrinkers, now that they are queued for +		 * destruction. +		 */ +		if (bo->mem.placement & TTM_PL_FLAG_NO_EVICT) { +			bo->mem.placement &= ~TTM_PL_FLAG_NO_EVICT; +			ttm_bo_add_to_lru(bo); +		} + +		__ttm_bo_unreserve(bo); +	}  	kref_get(&bo->list_kref);  	list_add_tail(&bo->ddestroy, &bdev->ddestroy); @@ -480,7 +494,7 @@ static int ttm_bo_cleanup_refs_and_unlock(struct ttm_buffer_object *bo,  		sync_obj = driver->sync_obj_ref(bo->sync_obj);  		spin_unlock(&bdev->fence_lock); -		ww_mutex_unlock(&bo->resv->lock); +		__ttm_bo_unreserve(bo);  		spin_unlock(&glob->lru_lock);  		ret = driver->sync_obj_wait(sync_obj, false, interruptible); @@ -500,7 +514,7 @@ static int ttm_bo_cleanup_refs_and_unlock(struct ttm_buffer_object *bo,  			return ret;  		spin_lock(&glob->lru_lock); -		ret = ttm_bo_reserve_nolru(bo, false, true, false, 0); +		ret = __ttm_bo_reserve(bo, false, true, false, 0);  		/*  		 * We raced, and lost, someone else holds the reservation now, @@ -518,7 +532,7 @@ static int ttm_bo_cleanup_refs_and_unlock(struct ttm_buffer_object *bo,  		spin_unlock(&bdev->fence_lock);  	if (ret || unlikely(list_empty(&bo->ddestroy))) { -		ww_mutex_unlock(&bo->resv->lock); +		__ttm_bo_unreserve(bo);  		spin_unlock(&glob->lru_lock);  		return ret;  	} @@ -563,11 +577,11 @@ static int ttm_bo_delayed_delete(struct ttm_bo_device *bdev, bool remove_all)  			kref_get(&nentry->list_kref);  		} -		ret = ttm_bo_reserve_nolru(entry, false, true, false, 0); +		ret = __ttm_bo_reserve(entry, false, true, false, 0);  		if (remove_all && ret) {  			spin_unlock(&glob->lru_lock); -			ret = ttm_bo_reserve_nolru(entry, false, false, -						   false, 0); +			ret = __ttm_bo_reserve(entry, false, false, +					       false, 0);  			spin_lock(&glob->lru_lock);  		} @@ -712,7 +726,7 @@ static int ttm_mem_evict_first(struct ttm_bo_device *bdev,  	spin_lock(&glob->lru_lock);  	list_for_each_entry(bo, &man->lru, lru) { -		ret = ttm_bo_reserve_nolru(bo, false, true, false, 0); +		ret = __ttm_bo_reserve(bo, false, true, false, 0);  		if (!ret)  			break;  	} @@ -945,7 +959,7 @@ int ttm_bo_mem_space(struct ttm_buffer_object *bo,  }  EXPORT_SYMBOL(ttm_bo_mem_space); -int ttm_bo_move_buffer(struct ttm_buffer_object *bo, +static int ttm_bo_move_buffer(struct ttm_buffer_object *bo,  			struct ttm_placement *placement,  			bool interruptible,  			bool no_wait_gpu) @@ -986,24 +1000,32 @@ out_unlock:  	return ret;  } -static int ttm_bo_mem_compat(struct ttm_placement *placement, -			     struct ttm_mem_reg *mem) +static bool ttm_bo_mem_compat(struct ttm_placement *placement, +			      struct ttm_mem_reg *mem, +			      uint32_t *new_flags)  {  	int i;  	if (mem->mm_node && placement->lpfn != 0 &&  	    (mem->start < placement->fpfn ||  	     mem->start + mem->num_pages > placement->lpfn)) -		return -1; +		return false;  	for (i = 0; i < placement->num_placement; i++) { -		if ((placement->placement[i] & mem->placement & -			TTM_PL_MASK_CACHING) && -			(placement->placement[i] & mem->placement & -			TTM_PL_MASK_MEM)) -			return i; +		*new_flags = placement->placement[i]; +		if ((*new_flags & mem->placement & TTM_PL_MASK_CACHING) && +		    (*new_flags & mem->placement & TTM_PL_MASK_MEM)) +			return true; +	} + +	for (i = 0; i < placement->num_busy_placement; i++) { +		*new_flags = placement->busy_placement[i]; +		if ((*new_flags & mem->placement & TTM_PL_MASK_CACHING) && +		    (*new_flags & mem->placement & TTM_PL_MASK_MEM)) +			return true;  	} -	return -1; + +	return false;  }  int ttm_bo_validate(struct ttm_buffer_object *bo, @@ -1012,6 +1034,7 @@ int ttm_bo_validate(struct ttm_buffer_object *bo,  			bool no_wait_gpu)  {  	int ret; +	uint32_t new_flags;  	lockdep_assert_held(&bo->resv->lock.base);  	/* Check that range is valid */ @@ -1022,8 +1045,7 @@ int ttm_bo_validate(struct ttm_buffer_object *bo,  	/*  	 * Check whether we need to move buffer.  	 */ -	ret = ttm_bo_mem_compat(placement, &bo->mem); -	if (ret < 0) { +	if (!ttm_bo_mem_compat(placement, &bo->mem, &new_flags)) {  		ret = ttm_bo_move_buffer(bo, placement, interruptible,  					 no_wait_gpu);  		if (ret) @@ -1033,7 +1055,7 @@ int ttm_bo_validate(struct ttm_buffer_object *bo,  		 * Use the access and other non-mapping-related flag bits from  		 * the compatible memory placement flags to the active flags  		 */ -		ttm_flag_masked(&bo->mem.placement, placement->placement[ret], +		ttm_flag_masked(&bo->mem.placement, new_flags,  				~TTM_PL_MASK_MEMTYPE);  	}  	/* @@ -1103,6 +1125,7 @@ int ttm_bo_init(struct ttm_bo_device *bdev,  	INIT_LIST_HEAD(&bo->ddestroy);  	INIT_LIST_HEAD(&bo->swap);  	INIT_LIST_HEAD(&bo->io_reserve_lru); +	mutex_init(&bo->wu_mutex);  	bo->bdev = bdev;  	bo->glob = bdev->glob;  	bo->type = type; @@ -1428,6 +1451,7 @@ EXPORT_SYMBOL(ttm_bo_device_release);  int ttm_bo_device_init(struct ttm_bo_device *bdev,  		       struct ttm_bo_global *glob,  		       struct ttm_bo_driver *driver, +		       struct address_space *mapping,  		       uint64_t file_page_offset,  		       bool need_dma32)  { @@ -1449,7 +1473,7 @@ int ttm_bo_device_init(struct ttm_bo_device *bdev,  				    0x10000000);  	INIT_DELAYED_WORK(&bdev->wq, ttm_bo_delayed_workqueue);  	INIT_LIST_HEAD(&bdev->ddestroy); -	bdev->dev_mapping = NULL; +	bdev->dev_mapping = mapping;  	bdev->glob = glob;  	bdev->need_dma32 = need_dma32;  	bdev->val_seq = 0; @@ -1606,7 +1630,7 @@ static int ttm_bo_swapout(struct ttm_mem_shrink *shrink)  	spin_lock(&glob->lru_lock);  	list_for_each_entry(bo, &glob->swap_lru, swap) { -		ret = ttm_bo_reserve_nolru(bo, false, true, false, 0); +		ret = __ttm_bo_reserve(bo, false, true, false, 0);  		if (!ret)  			break;  	} @@ -1673,7 +1697,7 @@ out:  	 * already swapped buffer.  	 */ -	ww_mutex_unlock(&bo->resv->lock); +	__ttm_bo_unreserve(bo);  	kref_put(&bo->list_kref, ttm_bo_release_list);  	return ret;  } @@ -1684,3 +1708,35 @@ void ttm_bo_swapout_all(struct ttm_bo_device *bdev)  		;  }  EXPORT_SYMBOL(ttm_bo_swapout_all); + +/** + * ttm_bo_wait_unreserved - interruptible wait for a buffer object to become + * unreserved + * + * @bo: Pointer to buffer + */ +int ttm_bo_wait_unreserved(struct ttm_buffer_object *bo) +{ +	int ret; + +	/* +	 * In the absense of a wait_unlocked API, +	 * Use the bo::wu_mutex to avoid triggering livelocks due to +	 * concurrent use of this function. Note that this use of +	 * bo::wu_mutex can go away if we change locking order to +	 * mmap_sem -> bo::reserve. +	 */ +	ret = mutex_lock_interruptible(&bo->wu_mutex); +	if (unlikely(ret != 0)) +		return -ERESTARTSYS; +	if (!ww_mutex_is_locked(&bo->resv->lock)) +		goto out_unlock; +	ret = __ttm_bo_reserve(bo, true, false, false, NULL); +	if (unlikely(ret != 0)) +		goto out_unlock; +	__ttm_bo_unreserve(bo); + +out_unlock: +	mutex_unlock(&bo->wu_mutex); +	return ret; +} diff --git a/drivers/gpu/drm/ttm/ttm_bo_manager.c b/drivers/gpu/drm/ttm/ttm_bo_manager.c index c58eba33bd5..bd850c9f4bc 100644 --- a/drivers/gpu/drm/ttm/ttm_bo_manager.c +++ b/drivers/gpu/drm/ttm/ttm_bo_manager.c @@ -55,6 +55,7 @@ static int ttm_bo_man_get_node(struct ttm_mem_type_manager *man,  	struct ttm_range_manager *rman = (struct ttm_range_manager *) man->priv;  	struct drm_mm *mm = &rman->mm;  	struct drm_mm_node *node = NULL; +	enum drm_mm_allocator_flags aflags = DRM_MM_CREATE_DEFAULT;  	unsigned long lpfn;  	int ret; @@ -66,11 +67,15 @@ static int ttm_bo_man_get_node(struct ttm_mem_type_manager *man,  	if (!node)  		return -ENOMEM; +	if (bo->mem.placement & TTM_PL_FLAG_TOPDOWN) +		aflags = DRM_MM_CREATE_TOP; +  	spin_lock(&rman->lock); -	ret = drm_mm_insert_node_in_range(mm, node, mem->num_pages, -					  mem->page_alignment, +	ret = drm_mm_insert_node_in_range_generic(mm, node, mem->num_pages, +					  mem->page_alignment, 0,  					  placement->fpfn, lpfn, -					  DRM_MM_SEARCH_BEST); +					  DRM_MM_SEARCH_BEST, +					  aflags);  	spin_unlock(&rman->lock);  	if (unlikely(ret)) { diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c b/drivers/gpu/drm/ttm/ttm_bo_util.c index 7cc904d3a4d..1df856f7856 100644 --- a/drivers/gpu/drm/ttm/ttm_bo_util.c +++ b/drivers/gpu/drm/ttm/ttm_bo_util.c @@ -187,7 +187,7 @@ void ttm_mem_io_free_vm(struct ttm_buffer_object *bo)  	}  } -int ttm_mem_reg_ioremap(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem, +static int ttm_mem_reg_ioremap(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem,  			void **virtual)  {  	struct ttm_mem_type_manager *man = &bdev->man[mem->mem_type]; @@ -219,7 +219,7 @@ int ttm_mem_reg_ioremap(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem,  	return 0;  } -void ttm_mem_reg_iounmap(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem, +static void ttm_mem_reg_iounmap(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem,  			 void *virtual)  {  	struct ttm_mem_type_manager *man; @@ -343,19 +343,29 @@ int ttm_bo_move_memcpy(struct ttm_buffer_object *bo,  	if (ret)  		goto out; +	/* +	 * Single TTM move. NOP. +	 */  	if (old_iomap == NULL && new_iomap == NULL)  		goto out2; -	if (old_iomap == NULL && ttm == NULL) + +	/* +	 * Don't move nonexistent data. Clear destination instead. +	 */ +	if (old_iomap == NULL && +	    (ttm == NULL || (ttm->state == tt_unpopulated && +			     !(ttm->page_flags & TTM_PAGE_FLAG_SWAPPED)))) { +		memset_io(new_iomap, 0, new_mem->num_pages*PAGE_SIZE);  		goto out2; +	} -	if (ttm->state == tt_unpopulated) { +	/* +	 * TTM might be null for moves within the same region. +	 */ +	if (ttm && ttm->state == tt_unpopulated) {  		ret = ttm->bdev->driver->ttm_tt_populate(ttm); -		if (ret) { -			/* if we fail here don't nuke the mm node -			 * as the bo still owns it */ -			old_copy.mm_node = NULL; +		if (ret)  			goto out1; -		}  	}  	add = 0; @@ -381,11 +391,8 @@ int ttm_bo_move_memcpy(struct ttm_buffer_object *bo,  						   prot);  		} else  			ret = ttm_copy_io_page(new_iomap, old_iomap, page); -		if (ret) { -			/* failing here, means keep old copy as-is */ -			old_copy.mm_node = NULL; +		if (ret)  			goto out1; -		}  	}  	mb();  out2: @@ -403,7 +410,12 @@ out1:  	ttm_mem_reg_iounmap(bdev, old_mem, new_iomap);  out:  	ttm_mem_reg_iounmap(bdev, &old_copy, old_iomap); -	ttm_bo_mem_put(bo, &old_copy); + +	/* +	 * On error, keep the mm node! +	 */ +	if (!ret) +		ttm_bo_mem_put(bo, &old_copy);  	return ret;  }  EXPORT_SYMBOL(ttm_bo_move_memcpy); @@ -582,7 +594,7 @@ int ttm_bo_kmap(struct ttm_buffer_object *bo,  	if (start_page > bo->num_pages)  		return -EINVAL;  #if 0 -	if (num_pages > 1 && !DRM_SUSER(DRM_CURPROC)) +	if (num_pages > 1 && !capable(CAP_SYS_ADMIN))  		return -EPERM;  #endif  	(void) ttm_mem_io_lock(man, false); diff --git a/drivers/gpu/drm/ttm/ttm_bo_vm.c b/drivers/gpu/drm/ttm/ttm_bo_vm.c index 1006c15445e..0ce48e5a9cb 100644 --- a/drivers/gpu/drm/ttm/ttm_bo_vm.c +++ b/drivers/gpu/drm/ttm/ttm_bo_vm.c @@ -41,6 +41,51 @@  #define TTM_BO_VM_NUM_PREFAULT 16 +static int ttm_bo_vm_fault_idle(struct ttm_buffer_object *bo, +				struct vm_area_struct *vma, +				struct vm_fault *vmf) +{ +	struct ttm_bo_device *bdev = bo->bdev; +	int ret = 0; + +	spin_lock(&bdev->fence_lock); +	if (likely(!test_bit(TTM_BO_PRIV_FLAG_MOVING, &bo->priv_flags))) +		goto out_unlock; + +	/* +	 * Quick non-stalling check for idle. +	 */ +	ret = ttm_bo_wait(bo, false, false, true); +	if (likely(ret == 0)) +		goto out_unlock; + +	/* +	 * If possible, avoid waiting for GPU with mmap_sem +	 * held. +	 */ +	if (vmf->flags & FAULT_FLAG_ALLOW_RETRY) { +		ret = VM_FAULT_RETRY; +		if (vmf->flags & FAULT_FLAG_RETRY_NOWAIT) +			goto out_unlock; + +		up_read(&vma->vm_mm->mmap_sem); +		(void) ttm_bo_wait(bo, false, true, false); +		goto out_unlock; +	} + +	/* +	 * Ordinary wait. +	 */ +	ret = ttm_bo_wait(bo, false, true, false); +	if (unlikely(ret != 0)) +		ret = (ret != -ERESTARTSYS) ? VM_FAULT_SIGBUS : +			VM_FAULT_NOPAGE; + +out_unlock: +	spin_unlock(&bdev->fence_lock); +	return ret; +} +  static int ttm_bo_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)  {  	struct ttm_buffer_object *bo = (struct ttm_buffer_object *) @@ -57,27 +102,51 @@ static int ttm_bo_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)  	int retval = VM_FAULT_NOPAGE;  	struct ttm_mem_type_manager *man =  		&bdev->man[bo->mem.mem_type]; +	struct vm_area_struct cvma;  	/*  	 * Work around locking order reversal in fault / nopfn  	 * between mmap_sem and bo_reserve: Perform a trylock operation -	 * for reserve, and if it fails, retry the fault after scheduling. +	 * for reserve, and if it fails, retry the fault after waiting +	 * for the buffer to become unreserved.  	 */ - -	ret = ttm_bo_reserve(bo, true, true, false, 0); +	ret = ttm_bo_reserve(bo, true, true, false, NULL);  	if (unlikely(ret != 0)) { -		if (ret == -EBUSY) -			set_need_resched(); +		if (ret != -EBUSY) +			return VM_FAULT_NOPAGE; + +		if (vmf->flags & FAULT_FLAG_ALLOW_RETRY) { +			if (!(vmf->flags & FAULT_FLAG_RETRY_NOWAIT)) { +				up_read(&vma->vm_mm->mmap_sem); +				(void) ttm_bo_wait_unreserved(bo); +			} + +			return VM_FAULT_RETRY; +		} + +		/* +		 * If we'd want to change locking order to +		 * mmap_sem -> bo::reserve, we'd use a blocking reserve here +		 * instead of retrying the fault... +		 */  		return VM_FAULT_NOPAGE;  	} +	/* +	 * Refuse to fault imported pages. This should be handled +	 * (if at all) by redirecting mmap to the exporter. +	 */ +	if (bo->ttm && (bo->ttm->page_flags & TTM_PAGE_FLAG_SG)) { +		retval = VM_FAULT_SIGBUS; +		goto out_unlock; +	} +  	if (bdev->driver->fault_reserve_notify) {  		ret = bdev->driver->fault_reserve_notify(bo);  		switch (ret) {  		case 0:  			break;  		case -EBUSY: -			set_need_resched();  		case -ERESTARTSYS:  			retval = VM_FAULT_NOPAGE;  			goto out_unlock; @@ -91,18 +160,11 @@ static int ttm_bo_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)  	 * Wait for buffer data in transit, due to a pipelined  	 * move.  	 */ - -	spin_lock(&bdev->fence_lock); -	if (test_bit(TTM_BO_PRIV_FLAG_MOVING, &bo->priv_flags)) { -		ret = ttm_bo_wait(bo, false, true, false); -		spin_unlock(&bdev->fence_lock); -		if (unlikely(ret != 0)) { -			retval = (ret != -ERESTARTSYS) ? -			    VM_FAULT_SIGBUS : VM_FAULT_NOPAGE; -			goto out_unlock; -		} -	} else -		spin_unlock(&bdev->fence_lock); +	ret = ttm_bo_vm_fault_idle(bo, vma, vmf); +	if (unlikely(ret != 0)) { +		retval = ret; +		goto out_unlock; +	}  	ret = ttm_mem_io_lock(man, true);  	if (unlikely(ret != 0)) { @@ -116,9 +178,9 @@ static int ttm_bo_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)  	}  	page_offset = ((address - vma->vm_start) >> PAGE_SHIFT) + -	    drm_vma_node_start(&bo->vma_node) - vma->vm_pgoff; -	page_last = vma_pages(vma) + -	    drm_vma_node_start(&bo->vma_node) - vma->vm_pgoff; +		vma->vm_pgoff - drm_vma_node_start(&bo->vma_node); +	page_last = vma_pages(vma) + vma->vm_pgoff - +		drm_vma_node_start(&bo->vma_node);  	if (unlikely(page_offset >= bo->num_pages)) {  		retval = VM_FAULT_SIGBUS; @@ -126,26 +188,21 @@ static int ttm_bo_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)  	}  	/* -	 * Strictly, we're not allowed to modify vma->vm_page_prot here, -	 * since the mmap_sem is only held in read mode. However, we -	 * modify only the caching bits of vma->vm_page_prot and -	 * consider those bits protected by -	 * the bo->mutex, as we should be the only writers. -	 * There shouldn't really be any readers of these bits except -	 * within vm_insert_mixed()? fork? -	 * -	 * TODO: Add a list of vmas to the bo, and change the -	 * vma->vm_page_prot when the object changes caching policy, with -	 * the correct locks held. +	 * Make a local vma copy to modify the page_prot member +	 * and vm_flags if necessary. The vma parameter is protected +	 * by mmap_sem in write mode.  	 */ +	cvma = *vma; +	cvma.vm_page_prot = vm_get_page_prot(cvma.vm_flags); +  	if (bo->mem.bus.is_iomem) { -		vma->vm_page_prot = ttm_io_prot(bo->mem.placement, -						vma->vm_page_prot); +		cvma.vm_page_prot = ttm_io_prot(bo->mem.placement, +						cvma.vm_page_prot);  	} else {  		ttm = bo->ttm; -		vma->vm_page_prot = (bo->mem.placement & TTM_PL_FLAG_CACHED) ? -		    vm_get_page_prot(vma->vm_flags) : -		    ttm_io_prot(bo->mem.placement, vma->vm_page_prot); +		if (!(bo->mem.placement & TTM_PL_FLAG_CACHED)) +			cvma.vm_page_prot = ttm_io_prot(bo->mem.placement, +							cvma.vm_page_prot);  		/* Allocate all page at once, most common usage */  		if (ttm->bdev->driver->ttm_tt_populate(ttm)) { @@ -169,10 +226,17 @@ static int ttm_bo_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)  			} else if (unlikely(!page)) {  				break;  			} +			page->mapping = vma->vm_file->f_mapping; +			page->index = drm_vma_node_start(&bo->vma_node) + +				page_offset;  			pfn = page_to_pfn(page);  		} -		ret = vm_insert_mixed(vma, address, pfn); +		if (vma->vm_flags & VM_MIXEDMAP) +			ret = vm_insert_mixed(&cvma, address, pfn); +		else +			ret = vm_insert_pfn(&cvma, address, pfn); +  		/*  		 * Somebody beat us to this PTE or prefaulting to  		 * an already populated PTE, or prefaulting error. @@ -202,6 +266,8 @@ static void ttm_bo_vm_open(struct vm_area_struct *vma)  	struct ttm_buffer_object *bo =  	    (struct ttm_buffer_object *)vma->vm_private_data; +	WARN_ON(bo->bdev->dev_mapping != vma->vm_file->f_mapping); +  	(void)ttm_bo_reference(bo);  } @@ -271,7 +337,16 @@ int ttm_bo_mmap(struct file *filp, struct vm_area_struct *vma,  	 */  	vma->vm_private_data = bo; -	vma->vm_flags |= VM_IO | VM_MIXEDMAP | VM_DONTEXPAND | VM_DONTDUMP; + +	/* +	 * We'd like to use VM_PFNMAP on shared mappings, where +	 * (vma->vm_flags & VM_SHARED) != 0, for performance reasons, +	 * but for some reason VM_PFNMAP + x86 PAT + write-combine is very +	 * bad for performance. Until that has been sorted out, use +	 * VM_MIXEDMAP on all mappings. See freedesktop.org bug #75719 +	 */ +	vma->vm_flags |= VM_MIXEDMAP; +	vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP;  	return 0;  out_unref:  	ttm_bo_unref(&bo); @@ -286,7 +361,8 @@ int ttm_fbdev_mmap(struct vm_area_struct *vma, struct ttm_buffer_object *bo)  	vma->vm_ops = &ttm_bo_vm_ops;  	vma->vm_private_data = ttm_bo_reference(bo); -	vma->vm_flags |= VM_IO | VM_MIXEDMAP | VM_DONTEXPAND; +	vma->vm_flags |= VM_MIXEDMAP; +	vma->vm_flags |= VM_IO | VM_DONTEXPAND;  	return 0;  }  EXPORT_SYMBOL(ttm_fbdev_mmap); diff --git a/drivers/gpu/drm/ttm/ttm_execbuf_util.c b/drivers/gpu/drm/ttm/ttm_execbuf_util.c index 6c911789ae5..e8dac875852 100644 --- a/drivers/gpu/drm/ttm/ttm_execbuf_util.c +++ b/drivers/gpu/drm/ttm/ttm_execbuf_util.c @@ -32,8 +32,7 @@  #include <linux/sched.h>  #include <linux/module.h> -static void ttm_eu_backoff_reservation_locked(struct list_head *list, -					      struct ww_acquire_ctx *ticket) +static void ttm_eu_backoff_reservation_locked(struct list_head *list)  {  	struct ttm_validate_buffer *entry; @@ -47,7 +46,7 @@ static void ttm_eu_backoff_reservation_locked(struct list_head *list,  			ttm_bo_add_to_lru(bo);  			entry->removed = false;  		} -		ww_mutex_unlock(&bo->resv->lock); +		__ttm_bo_unreserve(bo);  	}  } @@ -93,8 +92,9 @@ void ttm_eu_backoff_reservation(struct ww_acquire_ctx *ticket,  	entry = list_first_entry(list, struct ttm_validate_buffer, head);  	glob = entry->bo->glob;  	spin_lock(&glob->lru_lock); -	ttm_eu_backoff_reservation_locked(list, ticket); -	ww_acquire_fini(ticket); +	ttm_eu_backoff_reservation_locked(list); +	if (ticket) +		ww_acquire_fini(ticket);  	spin_unlock(&glob->lru_lock);  }  EXPORT_SYMBOL(ttm_eu_backoff_reservation); @@ -130,7 +130,8 @@ int ttm_eu_reserve_buffers(struct ww_acquire_ctx *ticket,  	entry = list_first_entry(list, struct ttm_validate_buffer, head);  	glob = entry->bo->glob; -	ww_acquire_init(ticket, &reservation_ww_class); +	if (ticket) +		ww_acquire_init(ticket, &reservation_ww_class);  retry:  	list_for_each_entry(entry, list, head) {  		struct ttm_buffer_object *bo = entry->bo; @@ -139,16 +140,17 @@ retry:  		if (entry->reserved)  			continue; - -		ret = ttm_bo_reserve_nolru(bo, true, false, true, ticket); +		ret = __ttm_bo_reserve(bo, true, (ticket == NULL), true, +				       ticket);  		if (ret == -EDEADLK) {  			/* uh oh, we lost out, drop every reservation and try  			 * to only reserve this buffer, then start over if  			 * this succeeds.  			 */ +			BUG_ON(ticket == NULL);  			spin_lock(&glob->lru_lock); -			ttm_eu_backoff_reservation_locked(list, ticket); +			ttm_eu_backoff_reservation_locked(list);  			spin_unlock(&glob->lru_lock);  			ttm_eu_list_ref_sub(list);  			ret = ww_mutex_lock_slow_interruptible(&bo->resv->lock, @@ -175,7 +177,8 @@ retry:  		}  	} -	ww_acquire_done(ticket); +	if (ticket) +		ww_acquire_done(ticket);  	spin_lock(&glob->lru_lock);  	ttm_eu_del_from_lru_locked(list);  	spin_unlock(&glob->lru_lock); @@ -184,12 +187,14 @@ retry:  err:  	spin_lock(&glob->lru_lock); -	ttm_eu_backoff_reservation_locked(list, ticket); +	ttm_eu_backoff_reservation_locked(list);  	spin_unlock(&glob->lru_lock);  	ttm_eu_list_ref_sub(list);  err_fini: -	ww_acquire_done(ticket); -	ww_acquire_fini(ticket); +	if (ticket) { +		ww_acquire_done(ticket); +		ww_acquire_fini(ticket); +	}  	return ret;  }  EXPORT_SYMBOL(ttm_eu_reserve_buffers); @@ -219,12 +224,13 @@ void ttm_eu_fence_buffer_objects(struct ww_acquire_ctx *ticket,  		entry->old_sync_obj = bo->sync_obj;  		bo->sync_obj = driver->sync_obj_ref(sync_obj);  		ttm_bo_add_to_lru(bo); -		ww_mutex_unlock(&bo->resv->lock); +		__ttm_bo_unreserve(bo);  		entry->reserved = false;  	}  	spin_unlock(&bdev->fence_lock);  	spin_unlock(&glob->lru_lock); -	ww_acquire_fini(ticket); +	if (ticket) +		ww_acquire_fini(ticket);  	list_for_each_entry(entry, list, head) {  		if (entry->old_sync_obj) diff --git a/drivers/gpu/drm/ttm/ttm_lock.c b/drivers/gpu/drm/ttm/ttm_lock.c index 3daa9a3930b..6a954544727 100644 --- a/drivers/gpu/drm/ttm/ttm_lock.c +++ b/drivers/gpu/drm/ttm/ttm_lock.c @@ -186,14 +186,6 @@ int ttm_write_lock(struct ttm_lock *lock, bool interruptible)  }  EXPORT_SYMBOL(ttm_write_lock); -void ttm_write_lock_downgrade(struct ttm_lock *lock) -{ -	spin_lock(&lock->lock); -	lock->rw = 1; -	wake_up_all(&lock->queue); -	spin_unlock(&lock->lock); -} -  static int __ttm_vt_unlock(struct ttm_lock *lock)  {  	int ret = 0; diff --git a/drivers/gpu/drm/ttm/ttm_object.c b/drivers/gpu/drm/ttm/ttm_object.c index a868176c258..d2a05335278 100644 --- a/drivers/gpu/drm/ttm/ttm_object.c +++ b/drivers/gpu/drm/ttm/ttm_object.c @@ -1,6 +1,6 @@  /**************************************************************************   * - * Copyright (c) 2009 VMware, Inc., Palo Alto, CA., USA + * Copyright (c) 2009-2013 VMware, Inc., Palo Alto, CA., USA   * All Rights Reserved.   *   * Permission is hereby granted, free of charge, to any person obtaining a @@ -26,6 +26,12 @@   **************************************************************************/  /*   * Authors: Thomas Hellstrom <thellstrom-at-vmware-dot-com> + * + * While no substantial code is shared, the prime code is inspired by + * drm_prime.c, with + * Authors: + *      Dave Airlie <airlied@redhat.com> + *      Rob Clark <rob.clark@linaro.org>   */  /** @file ttm_ref_object.c   * @@ -34,6 +40,7 @@   * and release on file close.   */ +  /**   * struct ttm_object_file   * @@ -61,7 +68,7 @@  struct ttm_object_file {  	struct ttm_object_device *tdev; -	rwlock_t lock; +	spinlock_t lock;  	struct list_head ref_list;  	struct drm_open_hash ref_hash[TTM_REF_NUM];  	struct kref refcount; @@ -84,6 +91,9 @@ struct ttm_object_device {  	struct drm_open_hash object_hash;  	atomic_t object_count;  	struct ttm_mem_global *mem_glob; +	struct dma_buf_ops ops; +	void (*dmabuf_release)(struct dma_buf *dma_buf); +	size_t dma_buf_size;  };  /** @@ -108,6 +118,7 @@ struct ttm_object_device {   */  struct ttm_ref_object { +	struct rcu_head rcu_head;  	struct drm_hash_item hash;  	struct list_head head;  	struct kref kref; @@ -116,6 +127,8 @@ struct ttm_ref_object {  	struct ttm_object_file *tfile;  }; +static void ttm_prime_dmabuf_release(struct dma_buf *dma_buf); +  static inline struct ttm_object_file *  ttm_object_file_ref(struct ttm_object_file *tfile)  { @@ -198,10 +211,9 @@ static void ttm_release_base(struct kref *kref)  	 * call_rcu() or ttm_base_object_kfree().  	 */ -	if (base->refcount_release) { -		ttm_object_file_unref(&base->tfile); +	ttm_object_file_unref(&base->tfile); +	if (base->refcount_release)  		base->refcount_release(&base); -	}  }  void ttm_base_object_unref(struct ttm_base_object **p_base) @@ -217,32 +229,92 @@ EXPORT_SYMBOL(ttm_base_object_unref);  struct ttm_base_object *ttm_base_object_lookup(struct ttm_object_file *tfile,  					       uint32_t key)  { -	struct ttm_object_device *tdev = tfile->tdev; -	struct ttm_base_object *uninitialized_var(base); +	struct ttm_base_object *base = NULL;  	struct drm_hash_item *hash; +	struct drm_open_hash *ht = &tfile->ref_hash[TTM_REF_USAGE];  	int ret;  	rcu_read_lock(); -	ret = drm_ht_find_item_rcu(&tdev->object_hash, key, &hash); +	ret = drm_ht_find_item_rcu(ht, key, &hash);  	if (likely(ret == 0)) { -		base = drm_hash_entry(hash, struct ttm_base_object, hash); -		ret = kref_get_unless_zero(&base->refcount) ? 0 : -EINVAL; +		base = drm_hash_entry(hash, struct ttm_ref_object, hash)->obj; +		if (!kref_get_unless_zero(&base->refcount)) +			base = NULL;  	}  	rcu_read_unlock(); -	if (unlikely(ret != 0)) -		return NULL; +	return base; +} +EXPORT_SYMBOL(ttm_base_object_lookup); -	if (tfile != base->tfile && !base->shareable) { -		pr_err("Attempted access of non-shareable object\n"); -		ttm_base_object_unref(&base); -		return NULL; +struct ttm_base_object * +ttm_base_object_lookup_for_ref(struct ttm_object_device *tdev, uint32_t key) +{ +	struct ttm_base_object *base = NULL; +	struct drm_hash_item *hash; +	struct drm_open_hash *ht = &tdev->object_hash; +	int ret; + +	rcu_read_lock(); +	ret = drm_ht_find_item_rcu(ht, key, &hash); + +	if (likely(ret == 0)) { +		base = drm_hash_entry(hash, struct ttm_base_object, hash); +		if (!kref_get_unless_zero(&base->refcount)) +			base = NULL;  	} +	rcu_read_unlock();  	return base;  } -EXPORT_SYMBOL(ttm_base_object_lookup); +EXPORT_SYMBOL(ttm_base_object_lookup_for_ref); + +/** + * ttm_ref_object_exists - Check whether a caller has a valid ref object + * (has opened) a base object. + * + * @tfile: Pointer to a struct ttm_object_file identifying the caller. + * @base: Pointer to a struct base object. + * + * Checks wether the caller identified by @tfile has put a valid USAGE + * reference object on the base object identified by @base. + */ +bool ttm_ref_object_exists(struct ttm_object_file *tfile, +			   struct ttm_base_object *base) +{ +	struct drm_open_hash *ht = &tfile->ref_hash[TTM_REF_USAGE]; +	struct drm_hash_item *hash; +	struct ttm_ref_object *ref; + +	rcu_read_lock(); +	if (unlikely(drm_ht_find_item_rcu(ht, base->hash.key, &hash) != 0)) +		goto out_false; + +	/* +	 * Verify that the ref object is really pointing to our base object. +	 * Our base object could actually be dead, and the ref object pointing +	 * to another base object with the same handle. +	 */ +	ref = drm_hash_entry(hash, struct ttm_ref_object, hash); +	if (unlikely(base != ref->obj)) +		goto out_false; + +	/* +	 * Verify that the ref->obj pointer was actually valid! +	 */ +	rmb(); +	if (unlikely(atomic_read(&ref->kref.refcount) == 0)) +		goto out_false; + +	rcu_read_unlock(); +	return true; + + out_false: +	rcu_read_unlock(); +	return false; +} +EXPORT_SYMBOL(ttm_ref_object_exists);  int ttm_ref_object_add(struct ttm_object_file *tfile,  		       struct ttm_base_object *base, @@ -254,21 +326,25 @@ int ttm_ref_object_add(struct ttm_object_file *tfile,  	struct ttm_mem_global *mem_glob = tfile->tdev->mem_glob;  	int ret = -EINVAL; +	if (base->tfile != tfile && !base->shareable) +		return -EPERM; +  	if (existed != NULL)  		*existed = true;  	while (ret == -EINVAL) { -		read_lock(&tfile->lock); -		ret = drm_ht_find_item(ht, base->hash.key, &hash); +		rcu_read_lock(); +		ret = drm_ht_find_item_rcu(ht, base->hash.key, &hash);  		if (ret == 0) {  			ref = drm_hash_entry(hash, struct ttm_ref_object, hash); -			kref_get(&ref->kref); -			read_unlock(&tfile->lock); -			break; +			if (kref_get_unless_zero(&ref->kref)) { +				rcu_read_unlock(); +				break; +			}  		} -		read_unlock(&tfile->lock); +		rcu_read_unlock();  		ret = ttm_mem_global_alloc(mem_glob, sizeof(*ref),  					   false, false);  		if (unlikely(ret != 0)) @@ -285,19 +361,19 @@ int ttm_ref_object_add(struct ttm_object_file *tfile,  		ref->ref_type = ref_type;  		kref_init(&ref->kref); -		write_lock(&tfile->lock); -		ret = drm_ht_insert_item(ht, &ref->hash); +		spin_lock(&tfile->lock); +		ret = drm_ht_insert_item_rcu(ht, &ref->hash);  		if (likely(ret == 0)) {  			list_add_tail(&ref->head, &tfile->ref_list);  			kref_get(&base->refcount); -			write_unlock(&tfile->lock); +			spin_unlock(&tfile->lock);  			if (existed != NULL)  				*existed = false;  			break;  		} -		write_unlock(&tfile->lock); +		spin_unlock(&tfile->lock);  		BUG_ON(ret != -EINVAL);  		ttm_mem_global_free(mem_glob, sizeof(*ref)); @@ -318,17 +394,17 @@ static void ttm_ref_object_release(struct kref *kref)  	struct ttm_mem_global *mem_glob = tfile->tdev->mem_glob;  	ht = &tfile->ref_hash[ref->ref_type]; -	(void)drm_ht_remove_item(ht, &ref->hash); +	(void)drm_ht_remove_item_rcu(ht, &ref->hash);  	list_del(&ref->head); -	write_unlock(&tfile->lock); +	spin_unlock(&tfile->lock);  	if (ref->ref_type != TTM_REF_USAGE && base->ref_obj_release)  		base->ref_obj_release(base, ref->ref_type);  	ttm_base_object_unref(&ref->obj);  	ttm_mem_global_free(mem_glob, sizeof(*ref)); -	kfree(ref); -	write_lock(&tfile->lock); +	kfree_rcu(ref, rcu_head); +	spin_lock(&tfile->lock);  }  int ttm_ref_object_base_unref(struct ttm_object_file *tfile, @@ -339,15 +415,15 @@ int ttm_ref_object_base_unref(struct ttm_object_file *tfile,  	struct drm_hash_item *hash;  	int ret; -	write_lock(&tfile->lock); +	spin_lock(&tfile->lock);  	ret = drm_ht_find_item(ht, key, &hash);  	if (unlikely(ret != 0)) { -		write_unlock(&tfile->lock); +		spin_unlock(&tfile->lock);  		return -EINVAL;  	}  	ref = drm_hash_entry(hash, struct ttm_ref_object, hash);  	kref_put(&ref->kref, ttm_ref_object_release); -	write_unlock(&tfile->lock); +	spin_unlock(&tfile->lock);  	return 0;  }  EXPORT_SYMBOL(ttm_ref_object_base_unref); @@ -360,7 +436,7 @@ void ttm_object_file_release(struct ttm_object_file **p_tfile)  	struct ttm_object_file *tfile = *p_tfile;  	*p_tfile = NULL; -	write_lock(&tfile->lock); +	spin_lock(&tfile->lock);  	/*  	 * Since we release the lock within the loop, we have to @@ -376,7 +452,7 @@ void ttm_object_file_release(struct ttm_object_file **p_tfile)  	for (i = 0; i < TTM_REF_NUM; ++i)  		drm_ht_remove(&tfile->ref_hash[i]); -	write_unlock(&tfile->lock); +	spin_unlock(&tfile->lock);  	ttm_object_file_unref(&tfile);  }  EXPORT_SYMBOL(ttm_object_file_release); @@ -392,7 +468,7 @@ struct ttm_object_file *ttm_object_file_init(struct ttm_object_device *tdev,  	if (unlikely(tfile == NULL))  		return NULL; -	rwlock_init(&tfile->lock); +	spin_lock_init(&tfile->lock);  	tfile->tdev = tdev;  	kref_init(&tfile->refcount);  	INIT_LIST_HEAD(&tfile->ref_list); @@ -416,9 +492,10 @@ out_err:  }  EXPORT_SYMBOL(ttm_object_file_init); -struct ttm_object_device *ttm_object_device_init(struct ttm_mem_global -						 *mem_glob, -						 unsigned int hash_order) +struct ttm_object_device * +ttm_object_device_init(struct ttm_mem_global *mem_glob, +		       unsigned int hash_order, +		       const struct dma_buf_ops *ops)  {  	struct ttm_object_device *tdev = kmalloc(sizeof(*tdev), GFP_KERNEL);  	int ret; @@ -430,10 +507,17 @@ struct ttm_object_device *ttm_object_device_init(struct ttm_mem_global  	spin_lock_init(&tdev->object_lock);  	atomic_set(&tdev->object_count, 0);  	ret = drm_ht_create(&tdev->object_hash, hash_order); +	if (ret != 0) +		goto out_no_object_hash; -	if (likely(ret == 0)) -		return tdev; +	tdev->ops = *ops; +	tdev->dmabuf_release = tdev->ops.release; +	tdev->ops.release = ttm_prime_dmabuf_release; +	tdev->dma_buf_size = ttm_round_pot(sizeof(struct dma_buf)) + +		ttm_round_pot(sizeof(struct file)); +	return tdev; +out_no_object_hash:  	kfree(tdev);  	return NULL;  } @@ -452,3 +536,225 @@ void ttm_object_device_release(struct ttm_object_device **p_tdev)  	kfree(tdev);  }  EXPORT_SYMBOL(ttm_object_device_release); + +/** + * get_dma_buf_unless_doomed - get a dma_buf reference if possible. + * + * @dma_buf: Non-refcounted pointer to a struct dma-buf. + * + * Obtain a file reference from a lookup structure that doesn't refcount + * the file, but synchronizes with its release method to make sure it has + * not been freed yet. See for example kref_get_unless_zero documentation. + * Returns true if refcounting succeeds, false otherwise. + * + * Nobody really wants this as a public API yet, so let it mature here + * for some time... + */ +static bool __must_check get_dma_buf_unless_doomed(struct dma_buf *dmabuf) +{ +	return atomic_long_inc_not_zero(&dmabuf->file->f_count) != 0L; +} + +/** + * ttm_prime_refcount_release - refcount release method for a prime object. + * + * @p_base: Pointer to ttm_base_object pointer. + * + * This is a wrapper that calls the refcount_release founction of the + * underlying object. At the same time it cleans up the prime object. + * This function is called when all references to the base object we + * derive from are gone. + */ +static void ttm_prime_refcount_release(struct ttm_base_object **p_base) +{ +	struct ttm_base_object *base = *p_base; +	struct ttm_prime_object *prime; + +	*p_base = NULL; +	prime = container_of(base, struct ttm_prime_object, base); +	BUG_ON(prime->dma_buf != NULL); +	mutex_destroy(&prime->mutex); +	if (prime->refcount_release) +		prime->refcount_release(&base); +} + +/** + * ttm_prime_dmabuf_release - Release method for the dma-bufs we export + * + * @dma_buf: + * + * This function first calls the dma_buf release method the driver + * provides. Then it cleans up our dma_buf pointer used for lookup, + * and finally releases the reference the dma_buf has on our base + * object. + */ +static void ttm_prime_dmabuf_release(struct dma_buf *dma_buf) +{ +	struct ttm_prime_object *prime = +		(struct ttm_prime_object *) dma_buf->priv; +	struct ttm_base_object *base = &prime->base; +	struct ttm_object_device *tdev = base->tfile->tdev; + +	if (tdev->dmabuf_release) +		tdev->dmabuf_release(dma_buf); +	mutex_lock(&prime->mutex); +	if (prime->dma_buf == dma_buf) +		prime->dma_buf = NULL; +	mutex_unlock(&prime->mutex); +	ttm_mem_global_free(tdev->mem_glob, tdev->dma_buf_size); +	ttm_base_object_unref(&base); +} + +/** + * ttm_prime_fd_to_handle - Get a base object handle from a prime fd + * + * @tfile: A struct ttm_object_file identifying the caller. + * @fd: The prime / dmabuf fd. + * @handle: The returned handle. + * + * This function returns a handle to an object that previously exported + * a dma-buf. Note that we don't handle imports yet, because we simply + * have no consumers of that implementation. + */ +int ttm_prime_fd_to_handle(struct ttm_object_file *tfile, +			   int fd, u32 *handle) +{ +	struct ttm_object_device *tdev = tfile->tdev; +	struct dma_buf *dma_buf; +	struct ttm_prime_object *prime; +	struct ttm_base_object *base; +	int ret; + +	dma_buf = dma_buf_get(fd); +	if (IS_ERR(dma_buf)) +		return PTR_ERR(dma_buf); + +	if (dma_buf->ops != &tdev->ops) +		return -ENOSYS; + +	prime = (struct ttm_prime_object *) dma_buf->priv; +	base = &prime->base; +	*handle = base->hash.key; +	ret = ttm_ref_object_add(tfile, base, TTM_REF_USAGE, NULL); + +	dma_buf_put(dma_buf); + +	return ret; +} +EXPORT_SYMBOL_GPL(ttm_prime_fd_to_handle); + +/** + * ttm_prime_handle_to_fd - Return a dma_buf fd from a ttm prime object + * + * @tfile: Struct ttm_object_file identifying the caller. + * @handle: Handle to the object we're exporting from. + * @flags: flags for dma-buf creation. We just pass them on. + * @prime_fd: The returned file descriptor. + * + */ +int ttm_prime_handle_to_fd(struct ttm_object_file *tfile, +			   uint32_t handle, uint32_t flags, +			   int *prime_fd) +{ +	struct ttm_object_device *tdev = tfile->tdev; +	struct ttm_base_object *base; +	struct dma_buf *dma_buf; +	struct ttm_prime_object *prime; +	int ret; + +	base = ttm_base_object_lookup(tfile, handle); +	if (unlikely(base == NULL || +		     base->object_type != ttm_prime_type)) { +		ret = -ENOENT; +		goto out_unref; +	} + +	prime = container_of(base, struct ttm_prime_object, base); +	if (unlikely(!base->shareable)) { +		ret = -EPERM; +		goto out_unref; +	} + +	ret = mutex_lock_interruptible(&prime->mutex); +	if (unlikely(ret != 0)) { +		ret = -ERESTARTSYS; +		goto out_unref; +	} + +	dma_buf = prime->dma_buf; +	if (!dma_buf || !get_dma_buf_unless_doomed(dma_buf)) { + +		/* +		 * Need to create a new dma_buf, with memory accounting. +		 */ +		ret = ttm_mem_global_alloc(tdev->mem_glob, tdev->dma_buf_size, +					   false, true); +		if (unlikely(ret != 0)) { +			mutex_unlock(&prime->mutex); +			goto out_unref; +		} + +		dma_buf = dma_buf_export(prime, &tdev->ops, +					 prime->size, flags); +		if (IS_ERR(dma_buf)) { +			ret = PTR_ERR(dma_buf); +			ttm_mem_global_free(tdev->mem_glob, +					    tdev->dma_buf_size); +			mutex_unlock(&prime->mutex); +			goto out_unref; +		} + +		/* +		 * dma_buf has taken the base object reference +		 */ +		base = NULL; +		prime->dma_buf = dma_buf; +	} +	mutex_unlock(&prime->mutex); + +	ret = dma_buf_fd(dma_buf, flags); +	if (ret >= 0) { +		*prime_fd = ret; +		ret = 0; +	} else +		dma_buf_put(dma_buf); + +out_unref: +	if (base) +		ttm_base_object_unref(&base); +	return ret; +} +EXPORT_SYMBOL_GPL(ttm_prime_handle_to_fd); + +/** + * ttm_prime_object_init - Initialize a ttm_prime_object + * + * @tfile: struct ttm_object_file identifying the caller + * @size: The size of the dma_bufs we export. + * @prime: The object to be initialized. + * @shareable: See ttm_base_object_init + * @type: See ttm_base_object_init + * @refcount_release: See ttm_base_object_init + * @ref_obj_release: See ttm_base_object_init + * + * Initializes an object which is compatible with the drm_prime model + * for data sharing between processes and devices. + */ +int ttm_prime_object_init(struct ttm_object_file *tfile, size_t size, +			  struct ttm_prime_object *prime, bool shareable, +			  enum ttm_object_type type, +			  void (*refcount_release) (struct ttm_base_object **), +			  void (*ref_obj_release) (struct ttm_base_object *, +						   enum ttm_ref_type ref_type)) +{ +	mutex_init(&prime->mutex); +	prime->size = PAGE_ALIGN(size); +	prime->real_type = type; +	prime->dma_buf = NULL; +	prime->refcount_release = refcount_release; +	return ttm_base_object_init(tfile, &prime->base, shareable, +				    ttm_prime_type, +				    ttm_prime_refcount_release, +				    ref_obj_release); +} +EXPORT_SYMBOL(ttm_prime_object_init); diff --git a/drivers/gpu/drm/ttm/ttm_page_alloc_dma.c b/drivers/gpu/drm/ttm/ttm_page_alloc_dma.c index 7957beeeaf7..fb8259f6983 100644 --- a/drivers/gpu/drm/ttm/ttm_page_alloc_dma.c +++ b/drivers/gpu/drm/ttm/ttm_page_alloc_dma.c @@ -33,6 +33,7 @@   *   when freed).   */ +#if defined(CONFIG_SWIOTLB) || defined(CONFIG_INTEL_IOMMU)  #define pr_fmt(fmt) "[TTM] " fmt  #include <linux/dma-mapping.h> @@ -1142,3 +1143,5 @@ int ttm_dma_page_alloc_debugfs(struct seq_file *m, void *data)  	return 0;  }  EXPORT_SYMBOL_GPL(ttm_dma_page_alloc_debugfs); + +#endif diff --git a/drivers/gpu/drm/ttm/ttm_tt.c b/drivers/gpu/drm/ttm/ttm_tt.c index 210d5036516..75f31909004 100644 --- a/drivers/gpu/drm/ttm/ttm_tt.c +++ b/drivers/gpu/drm/ttm/ttm_tt.c @@ -170,9 +170,8 @@ void ttm_tt_destroy(struct ttm_tt *ttm)  		ttm_tt_unbind(ttm);  	} -	if (ttm->state == tt_unbound) { -		ttm->bdev->driver->ttm_tt_unpopulate(ttm); -	} +	if (ttm->state == tt_unbound) +		ttm_tt_unpopulate(ttm);  	if (!(ttm->page_flags & TTM_PAGE_FLAG_PERSISTENT_SWAP) &&  	    ttm->swap_storage) @@ -362,7 +361,7 @@ int ttm_tt_swapout(struct ttm_tt *ttm, struct file *persistent_swap_storage)  		page_cache_release(to_page);  	} -	ttm->bdev->driver->ttm_tt_unpopulate(ttm); +	ttm_tt_unpopulate(ttm);  	ttm->swap_storage = swap_storage;  	ttm->page_flags |= TTM_PAGE_FLAG_SWAPPED;  	if (persistent_swap_storage) @@ -375,3 +374,26 @@ out_err:  	return ret;  } + +static void ttm_tt_clear_mapping(struct ttm_tt *ttm) +{ +	pgoff_t i; +	struct page **page = ttm->pages; + +	if (ttm->page_flags & TTM_PAGE_FLAG_SG) +		return; + +	for (i = 0; i < ttm->num_pages; ++i) { +		(*page)->mapping = NULL; +		(*page++)->index = 0; +	} +} + +void ttm_tt_unpopulate(struct ttm_tt *ttm) +{ +	if (ttm->state == tt_unpopulated) +		return; + +	ttm_tt_clear_mapping(ttm); +	ttm->bdev->driver->ttm_tt_unpopulate(ttm); +}  | 
