diff options
Diffstat (limited to 'drivers/gpu/drm/vmwgfx/vmwgfx_shader.c')
| -rw-r--r-- | drivers/gpu/drm/vmwgfx/vmwgfx_shader.c | 469 | 
1 files changed, 420 insertions, 49 deletions
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c b/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c index 1457ec4b7125..ee3856578a12 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c @@ -29,6 +29,8 @@  #include "vmwgfx_resource_priv.h"  #include "ttm/ttm_placement.h" +#define VMW_COMPAT_SHADER_HT_ORDER 12 +  struct vmw_shader {  	struct vmw_resource res;  	SVGA3dShaderType type; @@ -40,6 +42,50 @@ struct vmw_user_shader {  	struct vmw_shader shader;  }; +/** + * enum vmw_compat_shader_state - Staging state for compat shaders + */ +enum vmw_compat_shader_state { +	VMW_COMPAT_COMMITED, +	VMW_COMPAT_ADD, +	VMW_COMPAT_DEL +}; + +/** + * struct vmw_compat_shader - Metadata for compat shaders. + * + * @handle: The TTM handle of the guest backed shader. + * @tfile: The struct ttm_object_file the guest backed shader is registered + * with. + * @hash: Hash item for lookup. + * @head: List head for staging lists or the compat shader manager list. + * @state: Staging state. + * + * The structure is protected by the cmdbuf lock. + */ +struct vmw_compat_shader { +	u32 handle; +	struct ttm_object_file *tfile; +	struct drm_hash_item hash; +	struct list_head head; +	enum vmw_compat_shader_state state; +}; + +/** + * struct vmw_compat_shader_manager - Compat shader manager. + * + * @shaders: Hash table containing staged and commited compat shaders + * @list: List of commited shaders. + * @dev_priv: Pointer to a device private structure. + * + * @shaders and @list are protected by the cmdbuf mutex for now. + */ +struct vmw_compat_shader_manager { +	struct drm_open_hash shaders; +	struct list_head list; +	struct vmw_private *dev_priv; +}; +  static void vmw_user_shader_free(struct vmw_resource *res);  static struct vmw_resource *  vmw_user_shader_base_to_res(struct ttm_base_object *base); @@ -258,7 +304,7 @@ static int vmw_gb_shader_destroy(struct vmw_resource *res)  		return 0;  	mutex_lock(&dev_priv->binding_mutex); -	vmw_context_binding_res_list_kill(&res->binding_head); +	vmw_context_binding_res_list_scrub(&res->binding_head);  	cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd));  	if (unlikely(cmd == NULL)) { @@ -325,13 +371,81 @@ int vmw_shader_destroy_ioctl(struct drm_device *dev, void *data,  					 TTM_REF_USAGE);  } +static int vmw_shader_alloc(struct vmw_private *dev_priv, +			    struct vmw_dma_buffer *buffer, +			    size_t shader_size, +			    size_t offset, +			    SVGA3dShaderType shader_type, +			    struct ttm_object_file *tfile, +			    u32 *handle) +{ +	struct vmw_user_shader *ushader; +	struct vmw_resource *res, *tmp; +	int ret; + +	/* +	 * Approximate idr memory usage with 128 bytes. It will be limited +	 * by maximum number_of shaders anyway. +	 */ +	if (unlikely(vmw_user_shader_size == 0)) +		vmw_user_shader_size = +			ttm_round_pot(sizeof(struct vmw_user_shader)) + 128; + +	ret = ttm_mem_global_alloc(vmw_mem_glob(dev_priv), +				   vmw_user_shader_size, +				   false, true); +	if (unlikely(ret != 0)) { +		if (ret != -ERESTARTSYS) +			DRM_ERROR("Out of graphics memory for shader " +				  "creation.\n"); +		goto out; +	} + +	ushader = kzalloc(sizeof(*ushader), GFP_KERNEL); +	if (unlikely(ushader == NULL)) { +		ttm_mem_global_free(vmw_mem_glob(dev_priv), +				    vmw_user_shader_size); +		ret = -ENOMEM; +		goto out; +	} + +	res = &ushader->shader.res; +	ushader->base.shareable = false; +	ushader->base.tfile = NULL; + +	/* +	 * From here on, the destructor takes over resource freeing. +	 */ + +	ret = vmw_gb_shader_init(dev_priv, res, shader_size, +				 offset, shader_type, buffer, +				 vmw_user_shader_free); +	if (unlikely(ret != 0)) +		goto out; + +	tmp = vmw_resource_reference(res); +	ret = ttm_base_object_init(tfile, &ushader->base, false, +				   VMW_RES_SHADER, +				   &vmw_user_shader_base_release, NULL); + +	if (unlikely(ret != 0)) { +		vmw_resource_unreference(&tmp); +		goto out_err; +	} + +	if (handle) +		*handle = ushader->base.hash.key; +out_err: +	vmw_resource_unreference(&res); +out: +	return ret; +} + +  int vmw_shader_define_ioctl(struct drm_device *dev, void *data,  			     struct drm_file *file_priv)  {  	struct vmw_private *dev_priv = vmw_priv(dev); -	struct vmw_user_shader *ushader; -	struct vmw_resource *res; -	struct vmw_resource *tmp;  	struct drm_vmw_shader_create_arg *arg =  		(struct drm_vmw_shader_create_arg *)data;  	struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; @@ -373,69 +487,326 @@ int vmw_shader_define_ioctl(struct drm_device *dev, void *data,  		goto out_bad_arg;  	} -	/* -	 * Approximate idr memory usage with 128 bytes. It will be limited -	 * by maximum number_of shaders anyway. -	 */ +	ret = ttm_read_lock(&vmaster->lock, true); +	if (unlikely(ret != 0)) +		goto out_bad_arg; -	if (unlikely(vmw_user_shader_size == 0)) -		vmw_user_shader_size = ttm_round_pot(sizeof(*ushader)) -			+ 128; +	ret = vmw_shader_alloc(dev_priv, buffer, arg->size, arg->offset, +			       shader_type, tfile, &arg->shader_handle); -	ret = ttm_read_lock(&vmaster->lock, true); +	ttm_read_unlock(&vmaster->lock); +out_bad_arg: +	vmw_dmabuf_unreference(&buffer); +	return ret; +} + +/** + * vmw_compat_shader_lookup - Look up a compat shader + * + * @man: Pointer to the compat shader manager. + * @shader_type: The shader type, that combined with the user_key identifies + * the shader. + * @user_key: On entry, this should be a pointer to the user_key. + * On successful exit, it will contain the guest-backed shader's TTM handle. + * + * Returns 0 on success. Non-zero on failure, in which case the value pointed + * to by @user_key is unmodified. + */ +int vmw_compat_shader_lookup(struct vmw_compat_shader_manager *man, +			     SVGA3dShaderType shader_type, +			     u32 *user_key) +{ +	struct drm_hash_item *hash; +	int ret; +	unsigned long key = *user_key | (shader_type << 24); + +	ret = drm_ht_find_item(&man->shaders, key, &hash);  	if (unlikely(ret != 0))  		return ret; -	ret = ttm_mem_global_alloc(vmw_mem_glob(dev_priv), -				   vmw_user_shader_size, -				   false, true); -	if (unlikely(ret != 0)) { -		if (ret != -ERESTARTSYS) -			DRM_ERROR("Out of graphics memory for shader" -				  " creation.\n"); -		goto out_unlock; +	*user_key = drm_hash_entry(hash, struct vmw_compat_shader, +				   hash)->handle; + +	return 0; +} + +/** + * vmw_compat_shader_free - Free a compat shader. + * + * @man: Pointer to the compat shader manager. + * @entry: Pointer to a struct vmw_compat_shader. + * + * Frees a struct vmw_compat_shder entry and drops its reference to the + * guest backed shader. + */ +static void vmw_compat_shader_free(struct vmw_compat_shader_manager *man, +				   struct vmw_compat_shader *entry) +{ +	list_del(&entry->head); +	WARN_ON(drm_ht_remove_item(&man->shaders, &entry->hash)); +	WARN_ON(ttm_ref_object_base_unref(entry->tfile, entry->handle, +					  TTM_REF_USAGE)); +	kfree(entry); +} + +/** + * vmw_compat_shaders_commit - Commit a list of compat shader actions. + * + * @man: Pointer to the compat shader manager. + * @list: Caller's list of compat shader actions. + * + * This function commits a list of compat shader additions or removals. + * It is typically called when the execbuf ioctl call triggering these + * actions has commited the fifo contents to the device. + */ +void vmw_compat_shaders_commit(struct vmw_compat_shader_manager *man, +			       struct list_head *list) +{ +	struct vmw_compat_shader *entry, *next; + +	list_for_each_entry_safe(entry, next, list, head) { +		list_del(&entry->head); +		switch (entry->state) { +		case VMW_COMPAT_ADD: +			entry->state = VMW_COMPAT_COMMITED; +			list_add_tail(&entry->head, &man->list); +			break; +		case VMW_COMPAT_DEL: +			ttm_ref_object_base_unref(entry->tfile, entry->handle, +						  TTM_REF_USAGE); +			kfree(entry); +			break; +		default: +			BUG(); +			break; +		}  	} +} -	ushader = kzalloc(sizeof(*ushader), GFP_KERNEL); -	if (unlikely(ushader == NULL)) { -		ttm_mem_global_free(vmw_mem_glob(dev_priv), -				    vmw_user_shader_size); -		ret = -ENOMEM; -		goto out_unlock; +/** + * vmw_compat_shaders_revert - Revert a list of compat shader actions + * + * @man: Pointer to the compat shader manager. + * @list: Caller's list of compat shader actions. + * + * This function reverts a list of compat shader additions or removals. + * It is typically called when the execbuf ioctl call triggering these + * actions failed for some reason, and the command stream was never + * submitted. + */ +void vmw_compat_shaders_revert(struct vmw_compat_shader_manager *man, +			       struct list_head *list) +{ +	struct vmw_compat_shader *entry, *next; +	int ret; + +	list_for_each_entry_safe(entry, next, list, head) { +		switch (entry->state) { +		case VMW_COMPAT_ADD: +			vmw_compat_shader_free(man, entry); +			break; +		case VMW_COMPAT_DEL: +			ret = drm_ht_insert_item(&man->shaders, &entry->hash); +			list_del(&entry->head); +			list_add_tail(&entry->head, &man->list); +			entry->state = VMW_COMPAT_COMMITED; +			break; +		default: +			BUG(); +			break; +		}  	} +} -	res = &ushader->shader.res; -	ushader->base.shareable = false; -	ushader->base.tfile = NULL; +/** + * vmw_compat_shader_remove - Stage a compat shader for removal. + * + * @man: Pointer to the compat shader manager + * @user_key: The key that is used to identify the shader. The key is + * unique to the shader type. + * @shader_type: Shader type. + * @list: Caller's list of staged shader actions. + * + * This function stages a compat shader for removal and removes the key from + * the shader manager's hash table. If the shader was previously only staged + * for addition it is completely removed (But the execbuf code may keep a + * reference if it was bound to a context between addition and removal). If + * it was previously commited to the manager, it is staged for removal. + */ +int vmw_compat_shader_remove(struct vmw_compat_shader_manager *man, +			     u32 user_key, SVGA3dShaderType shader_type, +			     struct list_head *list) +{ +	struct vmw_compat_shader *entry; +	struct drm_hash_item *hash; +	int ret; -	/* -	 * From here on, the destructor takes over resource freeing. -	 */ +	ret = drm_ht_find_item(&man->shaders, user_key | (shader_type << 24), +			       &hash); +	if (likely(ret != 0)) +		return -EINVAL; -	ret = vmw_gb_shader_init(dev_priv, res, arg->size, -				 arg->offset, shader_type, buffer, -				 vmw_user_shader_free); +	entry = drm_hash_entry(hash, struct vmw_compat_shader, hash); + +	switch (entry->state) { +	case VMW_COMPAT_ADD: +		vmw_compat_shader_free(man, entry); +		break; +	case VMW_COMPAT_COMMITED: +		(void) drm_ht_remove_item(&man->shaders, &entry->hash); +		list_del(&entry->head); +		entry->state = VMW_COMPAT_DEL; +		list_add_tail(&entry->head, list); +		break; +	default: +		BUG(); +		break; +	} + +	return 0; +} + +/** + * vmw_compat_shader_add - Create a compat shader and add the + * key to the manager + * + * @man: Pointer to the compat shader manager + * @user_key: The key that is used to identify the shader. The key is + * unique to the shader type. + * @bytecode: Pointer to the bytecode of the shader. + * @shader_type: Shader type. + * @tfile: Pointer to a struct ttm_object_file that the guest-backed shader is + * to be created with. + * @list: Caller's list of staged shader actions. + * + * Note that only the key is added to the shader manager's hash table. + * The shader is not yet added to the shader manager's list of shaders. + */ +int vmw_compat_shader_add(struct vmw_compat_shader_manager *man, +			  u32 user_key, const void *bytecode, +			  SVGA3dShaderType shader_type, +			  size_t size, +			  struct ttm_object_file *tfile, +			  struct list_head *list) +{ +	struct vmw_dma_buffer *buf; +	struct ttm_bo_kmap_obj map; +	bool is_iomem; +	struct vmw_compat_shader *compat; +	u32 handle; +	int ret; + +	if (user_key > ((1 << 24) - 1) || (unsigned) shader_type > 16) +		return -EINVAL; + +	/* Allocate and pin a DMA buffer */ +	buf = kzalloc(sizeof(*buf), GFP_KERNEL); +	if (unlikely(buf == NULL)) +		return -ENOMEM; + +	ret = vmw_dmabuf_init(man->dev_priv, buf, size, &vmw_sys_ne_placement, +			      true, vmw_dmabuf_bo_free);  	if (unlikely(ret != 0)) -		goto out_unlock; +		goto out; -	tmp = vmw_resource_reference(res); -	ret = ttm_base_object_init(tfile, &ushader->base, false, -				   VMW_RES_SHADER, -				   &vmw_user_shader_base_release, NULL); +	ret = ttm_bo_reserve(&buf->base, false, true, false, NULL); +	if (unlikely(ret != 0)) +		goto no_reserve; +	/* Map and copy shader bytecode. */ +	ret = ttm_bo_kmap(&buf->base, 0, PAGE_ALIGN(size) >> PAGE_SHIFT, +			  &map);  	if (unlikely(ret != 0)) { -		vmw_resource_unreference(&tmp); -		goto out_err; +		ttm_bo_unreserve(&buf->base); +		goto no_reserve;  	} -	arg->shader_handle = ushader->base.hash.key; -out_err: -	vmw_resource_unreference(&res); -out_unlock: -	ttm_read_unlock(&vmaster->lock); -out_bad_arg: -	vmw_dmabuf_unreference(&buffer); +	memcpy(ttm_kmap_obj_virtual(&map, &is_iomem), bytecode, size); +	WARN_ON(is_iomem); + +	ttm_bo_kunmap(&map); +	ret = ttm_bo_validate(&buf->base, &vmw_sys_placement, false, true); +	WARN_ON(ret != 0); +	ttm_bo_unreserve(&buf->base); +	/* Create a guest-backed shader container backed by the dma buffer */ +	ret = vmw_shader_alloc(man->dev_priv, buf, size, 0, shader_type, +			       tfile, &handle); +	vmw_dmabuf_unreference(&buf); +	if (unlikely(ret != 0)) +		goto no_reserve; +	/* +	 * Create a compat shader structure and stage it for insertion +	 * in the manager +	 */ +	compat = kzalloc(sizeof(*compat), GFP_KERNEL); +	if (compat == NULL) +		goto no_compat; + +	compat->hash.key = user_key |  (shader_type << 24); +	ret = drm_ht_insert_item(&man->shaders, &compat->hash); +	if (unlikely(ret != 0)) +		goto out_invalid_key; + +	compat->state = VMW_COMPAT_ADD; +	compat->handle = handle; +	compat->tfile = tfile; +	list_add_tail(&compat->head, list); + +	return 0; + +out_invalid_key: +	kfree(compat); +no_compat: +	ttm_ref_object_base_unref(tfile, handle, TTM_REF_USAGE); +no_reserve: +out:  	return ret; +} + +/** + * vmw_compat_shader_man_create - Create a compat shader manager + * + * @dev_priv: Pointer to a device private structure. + * + * Typically done at file open time. If successful returns a pointer to a + * compat shader manager. Otherwise returns an error pointer. + */ +struct vmw_compat_shader_manager * +vmw_compat_shader_man_create(struct vmw_private *dev_priv) +{ +	struct vmw_compat_shader_manager *man; +	int ret; + +	man = kzalloc(sizeof(*man), GFP_KERNEL); +	if (man == NULL) +		return ERR_PTR(-ENOMEM); + +	man->dev_priv = dev_priv; +	INIT_LIST_HEAD(&man->list); +	ret = drm_ht_create(&man->shaders, VMW_COMPAT_SHADER_HT_ORDER); +	if (ret == 0) +		return man; + +	kfree(man); +	return ERR_PTR(ret); +} + +/** + * vmw_compat_shader_man_destroy - Destroy a compat shader manager + * + * @man: Pointer to the shader manager to destroy. + * + * Typically done at file close time. + */ +void vmw_compat_shader_man_destroy(struct vmw_compat_shader_manager *man) +{ +	struct vmw_compat_shader *entry, *next; + +	mutex_lock(&man->dev_priv->cmdbuf_mutex); +	list_for_each_entry_safe(entry, next, &man->list, head) +		vmw_compat_shader_free(man, entry); +	mutex_unlock(&man->dev_priv->cmdbuf_mutex); +	kfree(man);  }  | 
