[Intel-gfx] [RFC] libdrm_intel: Add support for userptr objects

Damien Lespiau damien.lespiau at intel.com
Thu Jun 19 13:13:20 CEST 2014


On Wed, Feb 26, 2014 at 04:41:41PM +0000, Tvrtko Ursulin wrote:
> From: Tvrtko Ursulin <tvrtko.ursulin at intel.com>
> 
> Allow userptr objects to be created and used via libdrm_intel.
> 
> At the moment tiling and mapping to GTT aperture is not supported
> due hardware limitations across different generations and uncertainty
> about its usefulness.
> 
> Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin at intel.com>
> ---
>  include/drm/i915_drm.h    |  16 +++++
>  intel/intel_bufmgr.c      |  13 ++++
>  intel/intel_bufmgr.h      |   5 ++
>  intel/intel_bufmgr_gem.c  | 154 +++++++++++++++++++++++++++++++++++++++++++++-
>  intel/intel_bufmgr_priv.h |  12 +++-
>  5 files changed, 198 insertions(+), 2 deletions(-)

Apart from couple of remarks below I couldn't find anything that would
prevent merging this. Well, except maybe that it'd be very nice to have
some feedback from someone using it, we do have an API/ABI guarantee on
libdrm after all.

-- 
Damien

> diff --git a/include/drm/i915_drm.h b/include/drm/i915_drm.h
> index 2f4eb8c..d32ef99 100644
> --- a/include/drm/i915_drm.h
> +++ b/include/drm/i915_drm.h
> @@ -223,6 +223,7 @@ typedef struct _drm_i915_sarea {
>  #define DRM_I915_GEM_GET_CACHING	0x30
>  #define DRM_I915_REG_READ		0x31
>  #define DRM_I915_GET_RESET_STATS	0x32
> +#define DRM_I915_GEM_USERPTR		0x34
>  
>  #define DRM_IOCTL_I915_INIT		DRM_IOW( DRM_COMMAND_BASE + DRM_I915_INIT, drm_i915_init_t)
>  #define DRM_IOCTL_I915_FLUSH		DRM_IO ( DRM_COMMAND_BASE + DRM_I915_FLUSH)
> @@ -273,6 +274,7 @@ typedef struct _drm_i915_sarea {
>  #define DRM_IOCTL_I915_GEM_CONTEXT_DESTROY	DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_CONTEXT_DESTROY, struct drm_i915_gem_context_destroy)
>  #define DRM_IOCTL_I915_REG_READ			DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_REG_READ, struct drm_i915_reg_read)
>  #define DRM_IOCTL_I915_GET_RESET_STATS		DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GET_RESET_STATS, struct drm_i915_reset_stats)
> +#define DRM_IOCTL_I915_GEM_USERPTR	DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_USERPTR,  struct drm_i915_gem_userptr)
>  
>  /* Allow drivers to submit batchbuffers directly to hardware, relying
>   * on the security mechanisms provided by hardware.
> @@ -498,6 +500,20 @@ struct drm_i915_gem_mmap_gtt {
>  	__u64 offset;
>  };
>  
> +struct drm_i915_gem_userptr {
> +	__u64 user_ptr;
> +	__u64 user_size;
> +	__u32 flags;
> +#define I915_USERPTR_READ_ONLY 0x1
> +#define I915_USERPTR_UNSYNCHRONIZED 0x80000000
> +	/**
> +	* Returned handle for the object.
> +	*
> +	* Object handles are nonzero.
> +	*/
> +	__u32 handle;
> +};
> +
>  struct drm_i915_gem_set_domain {
>  	/** Handle for the object */
>  	__u32 handle;
> diff --git a/intel/intel_bufmgr.c b/intel/intel_bufmgr.c
> index 905556f..7f3d795 100644
> --- a/intel/intel_bufmgr.c
> +++ b/intel/intel_bufmgr.c
> @@ -60,6 +60,19 @@ drm_intel_bo *drm_intel_bo_alloc_for_render(drm_intel_bufmgr *bufmgr,
>  	return bufmgr->bo_alloc_for_render(bufmgr, name, size, alignment);
>  }
>  
> +drm_intel_bo *drm_intel_bo_alloc_userptr(drm_intel_bufmgr *bufmgr,
> +					  const char *name, void *addr,
> +					  uint32_t tiling_mode,
> +					  uint32_t stride,
> +					  unsigned long size,
> +					  unsigned long flags)
> +{
> +	if (bufmgr->bo_alloc_userptr)
> +		return bufmgr->bo_alloc_userptr(bufmgr, name, addr, tiling_mode,
> +						stride, size, flags);
> +	return NULL;
> +}
> +
>  drm_intel_bo *
>  drm_intel_bo_alloc_tiled(drm_intel_bufmgr *bufmgr, const char *name,
>                          int x, int y, int cpp, uint32_t *tiling_mode,
> diff --git a/intel/intel_bufmgr.h b/intel/intel_bufmgr.h
> index 9383c72..be83a56 100644
> --- a/intel/intel_bufmgr.h
> +++ b/intel/intel_bufmgr.h
> @@ -113,6 +113,11 @@ drm_intel_bo *drm_intel_bo_alloc_for_render(drm_intel_bufmgr *bufmgr,
>  					    const char *name,
>  					    unsigned long size,
>  					    unsigned int alignment);
> +drm_intel_bo *drm_intel_bo_alloc_userptr(drm_intel_bufmgr *bufmgr,
> +					const char *name,
> +					void *addr, uint32_t tiling_mode,
> +					uint32_t stride, unsigned long size,
> +					unsigned long flags);
>  drm_intel_bo *drm_intel_bo_alloc_tiled(drm_intel_bufmgr *bufmgr,
>  				       const char *name,
>  				       int x, int y, int cpp,
> diff --git a/intel/intel_bufmgr_gem.c b/intel/intel_bufmgr_gem.c
> index 007a6d8..7cad945 100644
> --- a/intel/intel_bufmgr_gem.c
> +++ b/intel/intel_bufmgr_gem.c
> @@ -182,6 +182,11 @@ struct _drm_intel_bo_gem {
>  	void *mem_virtual;
>  	/** GTT virtual address for the buffer, saved across map/unmap cycles */
>  	void *gtt_virtual;
> +	/**
> +	 * Virtual address of the buffer allocated by user, used for userptr
> +	 * objects only.
> +	 */
> +	void *user_virtual;
>  	int map_count;
>  	drmMMListHead vma_list;
>  
> @@ -221,6 +226,11 @@ struct _drm_intel_bo_gem {
>  	bool idle;
>  
>  	/**
> +	 * Boolean of whether this buffer was allocated with userptr
> +	 */
> +	bool is_userptr;
> +
> +	/**
>  	 * Size in bytes of this buffer and its relocation descendents.
>  	 *
>  	 * Used to avoid costly tree walking in
> @@ -847,6 +857,80 @@ drm_intel_gem_bo_alloc_tiled(drm_intel_bufmgr *bufmgr, const char *name,
>  					       tiling, stride);
>  }
>  
> +static drm_intel_bo *
> +drm_intel_gem_bo_alloc_userptr(drm_intel_bufmgr *bufmgr,
> +				const char *name,
> +				void *addr,
> +				uint32_t tiling_mode,
> +				uint32_t stride,
> +				unsigned long size,
> +				unsigned long flags)
> +{
> +	drm_intel_bufmgr_gem *bufmgr_gem = (drm_intel_bufmgr_gem *) bufmgr;
> +	drm_intel_bo_gem *bo_gem;
> +	int ret;
> +	struct drm_i915_gem_userptr userptr;
> +
> +	/* Tiling with userptr surfaces is not supported
> +	 * on all hardware so refuse it for time being.
> +	 */
> +	if (tiling_mode != I915_TILING_NONE)
> +		return NULL;
> +
> +	bo_gem = calloc(1, sizeof(*bo_gem));
> +	if (!bo_gem)
> +		return NULL;
> +
> +	bo_gem->bo.size = size;
> +
> +	VG_CLEAR(userptr);
> +	userptr.user_ptr = (__u64)((unsigned long)addr);
> +	userptr.user_size = size;
> +	userptr.flags = flags;
> +
> +	ret = drmIoctl(bufmgr_gem->fd,
> +			DRM_IOCTL_I915_GEM_USERPTR,
> +			&userptr);
> +	if (ret != 0) {
> +		DBG("bo_create_userptr: "
> +		    "ioctl failed with user ptr %p size 0x%lx, "
> +		    "user flags 0x%lx\n", addr, size, flags);
> +		free(bo_gem);
> +		return NULL;
> +	}
> +
> +	bo_gem->gem_handle = userptr.handle;
> +	bo_gem->bo.handle = bo_gem->gem_handle;
> +	bo_gem->bo.bufmgr    = bufmgr;
> +	bo_gem->is_userptr   = true;
> +	bo_gem->bo.virtual   = addr;
> +	/* Save the address provided by user */
> +	bo_gem->user_virtual = addr;
> +	bo_gem->tiling_mode  = I915_TILING_NONE;
> +	bo_gem->swizzle_mode = I915_BIT_6_SWIZZLE_NONE;
> +	bo_gem->stride       = 0;
> +
> +	DRMINITLISTHEAD(&bo_gem->name_list);
> +	DRMINITLISTHEAD(&bo_gem->vma_list);
> +
> +	bo_gem->name = name;
> +	atomic_set(&bo_gem->refcount, 1);
> +	bo_gem->validate_index = -1;
> +	bo_gem->reloc_tree_fences = 0;
> +	bo_gem->used_as_reloc_target = false;
> +	bo_gem->has_error = false;
> +	bo_gem->reusable = false;
> +
> +	drm_intel_bo_gem_set_in_aperture_size(bufmgr_gem, bo_gem);
> +
> +	DBG("bo_create_userptr: "
> +	    "ptr %p buf %d (%s) size %ldb, stride 0x%x, tile mode %d\n",
> +		addr, bo_gem->gem_handle, bo_gem->name,
> +		size, stride, tiling_mode);
> +
> +	return &bo_gem->bo;
> +}
> +
>  /**
>   * Returns a drm_intel_bo wrapping the given buffer object handle.
>   *
> @@ -1173,6 +1257,12 @@ static int drm_intel_gem_bo_map(drm_intel_bo *bo, int write_enable)
>  	struct drm_i915_gem_set_domain set_domain;
>  	int ret;
>  
> +	if (bo_gem->is_userptr) {
> +		/* Return the same user ptr */
> +		bo->virtual = bo_gem->user_virtual;
> +		return 0;
> +	}
> +
>  	pthread_mutex_lock(&bufmgr_gem->lock);
>  
>  	if (bo_gem->map_count++ == 0)
> @@ -1241,6 +1331,9 @@ map_gtt(drm_intel_bo *bo)
>  	drm_intel_bo_gem *bo_gem = (drm_intel_bo_gem *) bo;
>  	int ret;
>  
> +	if (bo_gem->is_userptr)
> +		return -EINVAL;
> +
>  	if (bo_gem->map_count++ == 0)
>  		drm_intel_gem_bo_open_vma(bufmgr_gem, bo_gem);
>  
> @@ -1385,13 +1478,18 @@ int drm_intel_gem_bo_map_unsynchronized(drm_intel_bo *bo)
>  
>  static int drm_intel_gem_bo_unmap(drm_intel_bo *bo)
>  {
> -	drm_intel_bufmgr_gem *bufmgr_gem = (drm_intel_bufmgr_gem *) bo->bufmgr;
> +	drm_intel_bufmgr_gem *bufmgr_gem;
>  	drm_intel_bo_gem *bo_gem = (drm_intel_bo_gem *) bo;
>  	int ret = 0;
>  
>  	if (bo == NULL)
>  		return 0;
>  
> +	if (bo_gem->is_userptr)
> +		return 0;
> +
> +	bufmgr_gem = (drm_intel_bufmgr_gem *) bo->bufmgr;
> +
>  	pthread_mutex_lock(&bufmgr_gem->lock);
>  
>  	if (bo_gem->map_count <= 0) {
> @@ -1449,6 +1547,9 @@ drm_intel_gem_bo_subdata(drm_intel_bo *bo, unsigned long offset,
>  	struct drm_i915_gem_pwrite pwrite;
>  	int ret;
>  
> +	if (bo_gem->is_userptr)
> +		return -EINVAL;
> +
>  	VG_CLEAR(pwrite);
>  	pwrite.handle = bo_gem->gem_handle;
>  	pwrite.offset = offset;
> @@ -1501,6 +1602,9 @@ drm_intel_gem_bo_get_subdata(drm_intel_bo *bo, unsigned long offset,
>  	struct drm_i915_gem_pread pread;
>  	int ret;
>  
> +	if (bo_gem->is_userptr)
> +		return -EINVAL;
> +
>  	VG_CLEAR(pread);
>  	pread.handle = bo_gem->gem_handle;
>  	pread.offset = offset;
> @@ -2460,6 +2564,12 @@ drm_intel_gem_bo_set_tiling(drm_intel_bo *bo, uint32_t * tiling_mode,
>  	drm_intel_bo_gem *bo_gem = (drm_intel_bo_gem *) bo;
>  	int ret;
>  
> +	/* Tiling with userptr surfaces is not supported
> +	 * on all hardware so refuse it for time being.
> +	 */
> +	if (bo_gem->is_userptr)
> +		return -EINVAL;
> +
>  	/* Linear buffers have no stride. By ensuring that we only ever use
>  	 * stride 0 with linear buffers, we simplify our code.
>  	 */
> @@ -3181,6 +3291,44 @@ drm_intel_bufmgr_gem_set_aub_annotations(drm_intel_bo *bo,
>  	bo_gem->aub_annotation_count = count;
>  }
>  
> +static bool
> +has_userptr(int fd)
> +{
> +	int ret;
> +	void *ptr;
> +	long pgsz;
> +	struct drm_i915_gem_userptr userptr;
> +	struct drm_gem_close close_bo;
> +
> +	pgsz = sysconf(_SC_PAGESIZE);
> +	assert(ret > 0);

You're asserting on an non initialized variable.

> +
> +	ret = posix_memalign(&ptr, pgsz, pgsz);
> +	assert(ret == 0);

In general I'd avoid asserting in a library when it's not about
asserting an invariant and properly handle the error case (by returning
false here).

> +
> +	memset(&userptr, 0, sizeof(userptr));
> +	userptr.user_ptr = (__u64)(unsigned long)ptr;
> +	userptr.user_size = pgsz;
> +
> +retry:
> +	ret = drmIoctl(fd, DRM_IOCTL_I915_GEM_USERPTR, &userptr);
> +	if (ret) {
> +		if (errno == ENODEV && userptr.flags == 0) {
> +			userptr.flags = I915_USERPTR_UNSYNCHRONIZED;
> +			goto retry;
> +		}
> +		free(ptr);
> +		return false;
> +	}
> +
> +	close_bo.handle = userptr.handle;
> +	ret = drmIoctl(fd, DRM_IOCTL_GEM_CLOSE, &close_bo);
> +	assert(ret == 0);

Same remark.

> +	free(ptr);
> +
> +	return true;
> +}
> +
>  /**
>   * Initializes the GEM buffer manager, which uses the kernel to allocate, map,
>   * and manage map buffer objections.
> @@ -3273,6 +3421,10 @@ drm_intel_bufmgr_gem_init(int fd, int batch_size)
>  	ret = drmIoctl(bufmgr_gem->fd, DRM_IOCTL_I915_GETPARAM, &gp);
>  	bufmgr_gem->has_relaxed_fencing = ret == 0;
>  
> +	if (has_userptr(fd))
> +		bufmgr_gem->bufmgr.bo_alloc_userptr =
> +			drm_intel_gem_bo_alloc_userptr;
> +
>  	gp.param = I915_PARAM_HAS_WAIT_TIMEOUT;
>  	ret = drmIoctl(bufmgr_gem->fd, DRM_IOCTL_I915_GETPARAM, &gp);
>  	bufmgr_gem->has_wait_timeout = ret == 0;
> diff --git a/intel/intel_bufmgr_priv.h b/intel/intel_bufmgr_priv.h
> index 2592d42..3aa1abb 100644
> --- a/intel/intel_bufmgr_priv.h
> +++ b/intel/intel_bufmgr_priv.h
> @@ -60,7 +60,17 @@ struct _drm_intel_bufmgr {
>  					      const char *name,
>  					      unsigned long size,
>  					      unsigned int alignment);
> -
> +	/**
> +	 * Allocate a buffer object from an existing user accessible
> +	 * address malloc'd with the provided size.
> +	 * Alignment is used when mapping to the gtt.
> +	 * Flags may be I915_VMAP_READ_ONLY or I915_USERPTR_UNSYNCHRONIZED
> +	 */
> +	drm_intel_bo *(*bo_alloc_userptr)(drm_intel_bufmgr *bufmgr,
> +					  const char *name, void *addr,
> +					  uint32_t tiling_mode, uint32_t stride,
> +					  unsigned long size,
> +					  unsigned long flags);
>  	/**
>  	 * Allocate a tiled buffer object.
>  	 *
> -- 
> 1.9.0
> 
> _______________________________________________
> Intel-gfx mailing list
> Intel-gfx at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/intel-gfx



More information about the Intel-gfx mailing list