diff options
Diffstat (limited to 'drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c')
| -rw-r--r-- | drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c | 898 | 
1 files changed, 650 insertions, 248 deletions
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c index e5775a0db49..534c96703c3 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c @@ -30,6 +30,181 @@  #include <drm/ttm/ttm_bo_api.h>  #include <drm/ttm/ttm_placement.h> +#define VMW_RES_HT_ORDER 12 + +/** + * struct vmw_resource_relocation - Relocation info for resources + * + * @head: List head for the software context's relocation list. + * @res: Non-ref-counted pointer to the resource. + * @offset: Offset of 4 byte entries into the command buffer where the + * id that needs fixup is located. + */ +struct vmw_resource_relocation { +	struct list_head head; +	const struct vmw_resource *res; +	unsigned long offset; +}; + +/** + * struct vmw_resource_val_node - Validation info for resources + * + * @head: List head for the software context's resource list. + * @hash: Hash entry for quick resouce to val_node lookup. + * @res: Ref-counted pointer to the resource. + * @switch_backup: Boolean whether to switch backup buffer on unreserve. + * @new_backup: Refcounted pointer to the new backup buffer. + * @new_backup_offset: New backup buffer offset if @new_backup is non-NUll. + * @first_usage: Set to true the first time the resource is referenced in + * the command stream. + * @no_buffer_needed: Resources do not need to allocate buffer backup on + * reservation. The command stream will provide one. + */ +struct vmw_resource_val_node { +	struct list_head head; +	struct drm_hash_item hash; +	struct vmw_resource *res; +	struct vmw_dma_buffer *new_backup; +	unsigned long new_backup_offset; +	bool first_usage; +	bool no_buffer_needed; +}; + +/** + * vmw_resource_unreserve - unreserve resources previously reserved for + * command submission. + * + * @list_head: list of resources to unreserve. + * @backoff: Whether command submission failed. + */ +static void vmw_resource_list_unreserve(struct list_head *list, +					bool backoff) +{ +	struct vmw_resource_val_node *val; + +	list_for_each_entry(val, list, head) { +		struct vmw_resource *res = val->res; +		struct vmw_dma_buffer *new_backup = +			backoff ? NULL : val->new_backup; + +		vmw_resource_unreserve(res, new_backup, +			val->new_backup_offset); +		vmw_dmabuf_unreference(&val->new_backup); +	} +} + + +/** + * vmw_resource_val_add - Add a resource to the software context's + * resource list if it's not already on it. + * + * @sw_context: Pointer to the software context. + * @res: Pointer to the resource. + * @p_node On successful return points to a valid pointer to a + * struct vmw_resource_val_node, if non-NULL on entry. + */ +static int vmw_resource_val_add(struct vmw_sw_context *sw_context, +				struct vmw_resource *res, +				struct vmw_resource_val_node **p_node) +{ +	struct vmw_resource_val_node *node; +	struct drm_hash_item *hash; +	int ret; + +	if (likely(drm_ht_find_item(&sw_context->res_ht, (unsigned long) res, +				    &hash) == 0)) { +		node = container_of(hash, struct vmw_resource_val_node, hash); +		node->first_usage = false; +		if (unlikely(p_node != NULL)) +			*p_node = node; +		return 0; +	} + +	node = kzalloc(sizeof(*node), GFP_KERNEL); +	if (unlikely(node == NULL)) { +		DRM_ERROR("Failed to allocate a resource validation " +			  "entry.\n"); +		return -ENOMEM; +	} + +	node->hash.key = (unsigned long) res; +	ret = drm_ht_insert_item(&sw_context->res_ht, &node->hash); +	if (unlikely(ret != 0)) { +		DRM_ERROR("Failed to initialize a resource validation " +			  "entry.\n"); +		kfree(node); +		return ret; +	} +	list_add_tail(&node->head, &sw_context->resource_list); +	node->res = vmw_resource_reference(res); +	node->first_usage = true; + +	if (unlikely(p_node != NULL)) +		*p_node = node; + +	return 0; +} + +/** + * vmw_resource_relocation_add - Add a relocation to the relocation list + * + * @list: Pointer to head of relocation list. + * @res: The resource. + * @offset: Offset into the command buffer currently being parsed where the + * id that needs fixup is located. Granularity is 4 bytes. + */ +static int vmw_resource_relocation_add(struct list_head *list, +				       const struct vmw_resource *res, +				       unsigned long offset) +{ +	struct vmw_resource_relocation *rel; + +	rel = kmalloc(sizeof(*rel), GFP_KERNEL); +	if (unlikely(rel == NULL)) { +		DRM_ERROR("Failed to allocate a resource relocation.\n"); +		return -ENOMEM; +	} + +	rel->res = res; +	rel->offset = offset; +	list_add_tail(&rel->head, list); + +	return 0; +} + +/** + * vmw_resource_relocations_free - Free all relocations on a list + * + * @list: Pointer to the head of the relocation list. + */ +static void vmw_resource_relocations_free(struct list_head *list) +{ +	struct vmw_resource_relocation *rel, *n; + +	list_for_each_entry_safe(rel, n, list, head) { +		list_del(&rel->head); +		kfree(rel); +	} +} + +/** + * vmw_resource_relocations_apply - Apply all relocations on a list + * + * @cb: Pointer to the start of the command buffer bein patch. This need + * not be the same buffer as the one being parsed when the relocation + * list was built, but the contents must be the same modulo the + * resource ids. + * @list: Pointer to the head of the relocation list. + */ +static void vmw_resource_relocations_apply(uint32_t *cb, +					   struct list_head *list) +{ +	struct vmw_resource_relocation *rel; + +	list_for_each_entry(rel, list, head) +		cb[rel->offset] = rel->res->id; +} +  static int vmw_cmd_invalid(struct vmw_private *dev_priv,  			   struct vmw_sw_context *sw_context,  			   SVGA3dCmdHeader *header) @@ -44,25 +219,11 @@ static int vmw_cmd_ok(struct vmw_private *dev_priv,  	return 0;  } -static void vmw_resource_to_validate_list(struct vmw_sw_context *sw_context, -					  struct vmw_resource **p_res) -{ -	struct vmw_resource *res = *p_res; - -	if (list_empty(&res->validate_head)) { -		list_add_tail(&res->validate_head, &sw_context->resource_list); -		*p_res = NULL; -	} else -		vmw_resource_unreference(p_res); -} -  /**   * vmw_bo_to_validate_list - add a bo to a validate list   *   * @sw_context: The software context used for this command submission batch.   * @bo: The buffer object to add. - * @fence_flags: Fence flags to be or'ed with any other fence flags for - * this buffer on this submission batch.   * @p_val_node: If non-NULL Will be updated with the validate node number   * on return.   * @@ -74,21 +235,37 @@ static int vmw_bo_to_validate_list(struct vmw_sw_context *sw_context,  				   uint32_t *p_val_node)  {  	uint32_t val_node; +	struct vmw_validate_buffer *vval_buf;  	struct ttm_validate_buffer *val_buf; +	struct drm_hash_item *hash; +	int ret; -	val_node = vmw_dmabuf_validate_node(bo, sw_context->cur_val_buf); - -	if (unlikely(val_node >= VMWGFX_MAX_VALIDATIONS)) { -		DRM_ERROR("Max number of DMA buffers per submission" -			  " exceeded.\n"); -		return -EINVAL; -	} - -	val_buf = &sw_context->val_bufs[val_node]; -	if (unlikely(val_node == sw_context->cur_val_buf)) { +	if (likely(drm_ht_find_item(&sw_context->res_ht, (unsigned long) bo, +				    &hash) == 0)) { +		vval_buf = container_of(hash, struct vmw_validate_buffer, +					hash); +		val_buf = &vval_buf->base; +		val_node = vval_buf - sw_context->val_bufs; +	} else { +		val_node = sw_context->cur_val_buf; +		if (unlikely(val_node >= VMWGFX_MAX_VALIDATIONS)) { +			DRM_ERROR("Max number of DMA buffers per submission " +				  "exceeded.\n"); +			return -EINVAL; +		} +		vval_buf = &sw_context->val_bufs[val_node]; +		vval_buf->hash.key = (unsigned long) bo; +		ret = drm_ht_insert_item(&sw_context->res_ht, &vval_buf->hash); +		if (unlikely(ret != 0)) { +			DRM_ERROR("Failed to initialize a buffer validation " +				  "entry.\n"); +			return ret; +		} +		++sw_context->cur_val_buf; +		val_buf = &vval_buf->base;  		val_buf->bo = ttm_bo_reference(bo); +		val_buf->reserved = false;  		list_add_tail(&val_buf->head, &sw_context->validate_nodes); -		++sw_context->cur_val_buf;  	}  	sw_context->fence_flags |= DRM_VMW_FENCE_FLAG_EXEC; @@ -99,85 +276,174 @@ static int vmw_bo_to_validate_list(struct vmw_sw_context *sw_context,  	return 0;  } -static int vmw_cmd_cid_check(struct vmw_private *dev_priv, -			     struct vmw_sw_context *sw_context, -			     SVGA3dCmdHeader *header) +/** + * vmw_resources_reserve - Reserve all resources on the sw_context's + * resource list. + * + * @sw_context: Pointer to the software context. + * + * Note that since vmware's command submission currently is protected by + * the cmdbuf mutex, no fancy deadlock avoidance is required for resources, + * since only a single thread at once will attempt this. + */ +static int vmw_resources_reserve(struct vmw_sw_context *sw_context)  { -	struct vmw_resource *ctx; - -	struct vmw_cid_cmd { -		SVGA3dCmdHeader header; -		__le32 cid; -	} *cmd; +	struct vmw_resource_val_node *val;  	int ret; -	cmd = container_of(header, struct vmw_cid_cmd, header); -	if (likely(sw_context->cid_valid && cmd->cid == sw_context->last_cid)) -		return 0; +	list_for_each_entry(val, &sw_context->resource_list, head) { +		struct vmw_resource *res = val->res; -	ret = vmw_context_check(dev_priv, sw_context->tfile, cmd->cid, -				&ctx); -	if (unlikely(ret != 0)) { -		DRM_ERROR("Could not find or use context %u\n", -			  (unsigned) cmd->cid); -		return ret; +		ret = vmw_resource_reserve(res, val->no_buffer_needed); +		if (unlikely(ret != 0)) +			return ret; + +		if (res->backup) { +			struct ttm_buffer_object *bo = &res->backup->base; + +			ret = vmw_bo_to_validate_list +				(sw_context, bo, NULL); + +			if (unlikely(ret != 0)) +				return ret; +		}  	} +	return 0; +} -	sw_context->last_cid = cmd->cid; -	sw_context->cid_valid = true; -	sw_context->cur_ctx = ctx; -	vmw_resource_to_validate_list(sw_context, &ctx); +/** + * vmw_resources_validate - Validate all resources on the sw_context's + * resource list. + * + * @sw_context: Pointer to the software context. + * + * Before this function is called, all resource backup buffers must have + * been validated. + */ +static int vmw_resources_validate(struct vmw_sw_context *sw_context) +{ +	struct vmw_resource_val_node *val; +	int ret; + +	list_for_each_entry(val, &sw_context->resource_list, head) { +		struct vmw_resource *res = val->res; +		ret = vmw_resource_validate(res); +		if (unlikely(ret != 0)) { +			if (ret != -ERESTARTSYS) +				DRM_ERROR("Failed to validate resource.\n"); +			return ret; +		} +	}  	return 0;  } -static int vmw_cmd_sid_check(struct vmw_private *dev_priv, +/** + * vmw_cmd_res_check - Check that a resource is present and if so, put it + * on the resource validate list unless it's already there. + * + * @dev_priv: Pointer to a device private structure. + * @sw_context: Pointer to the software context. + * @res_type: Resource type. + * @converter: User-space visisble type specific information. + * @id: Pointer to the location in the command buffer currently being + * parsed from where the user-space resource id handle is located. + */ +static int vmw_cmd_res_check(struct vmw_private *dev_priv,  			     struct vmw_sw_context *sw_context, -			     uint32_t *sid) +			     enum vmw_res_type res_type, +			     const struct vmw_user_resource_conv *converter, +			     uint32_t *id, +			     struct vmw_resource_val_node **p_val)  { -	struct vmw_surface *srf; -	int ret; +	struct vmw_res_cache_entry *rcache = +		&sw_context->res_cache[res_type];  	struct vmw_resource *res; +	struct vmw_resource_val_node *node; +	int ret; -	if (*sid == SVGA3D_INVALID_ID) +	if (*id == SVGA3D_INVALID_ID)  		return 0; -	if (likely((sw_context->sid_valid  && -		      *sid == sw_context->last_sid))) { -		*sid = sw_context->sid_translation; -		return 0; -	} +	/* +	 * Fastpath in case of repeated commands referencing the same +	 * resource +	 */ -	ret = vmw_user_surface_lookup_handle(dev_priv, -					     sw_context->tfile, -					     *sid, &srf); -	if (unlikely(ret != 0)) { -		DRM_ERROR("Could ot find or use surface 0x%08x " -			  "address 0x%08lx\n", -			  (unsigned int) *sid, -			  (unsigned long) sid); -		return ret; +	if (likely(rcache->valid && *id == rcache->handle)) { +		const struct vmw_resource *res = rcache->res; + +		rcache->node->first_usage = false; +		if (p_val) +			*p_val = rcache->node; + +		return vmw_resource_relocation_add +			(&sw_context->res_relocations, res, +			 id - sw_context->buf_start);  	} -	ret = vmw_surface_validate(dev_priv, srf); +	ret = vmw_user_resource_lookup_handle(dev_priv, +					      sw_context->tfile, +					      *id, +					      converter, +					      &res);  	if (unlikely(ret != 0)) { -		if (ret != -ERESTARTSYS) -			DRM_ERROR("Could not validate surface.\n"); -		vmw_surface_unreference(&srf); +		DRM_ERROR("Could not find or use resource 0x%08x.\n", +			  (unsigned) *id); +		dump_stack();  		return ret;  	} -	sw_context->last_sid = *sid; -	sw_context->sid_valid = true; -	sw_context->sid_translation = srf->res.id; -	*sid = sw_context->sid_translation; +	rcache->valid = true; +	rcache->res = res; +	rcache->handle = *id; -	res = &srf->res; -	vmw_resource_to_validate_list(sw_context, &res); +	ret = vmw_resource_relocation_add(&sw_context->res_relocations, +					  res, +					  id - sw_context->buf_start); +	if (unlikely(ret != 0)) +		goto out_no_reloc; + +	ret = vmw_resource_val_add(sw_context, res, &node); +	if (unlikely(ret != 0)) +		goto out_no_reloc; +	rcache->node = node; +	if (p_val) +		*p_val = node; +	vmw_resource_unreference(&res);  	return 0; + +out_no_reloc: +	BUG_ON(sw_context->error_resource != NULL); +	sw_context->error_resource = res; + +	return ret;  } +/** + * vmw_cmd_cid_check - Check a command header for valid context information. + * + * @dev_priv: Pointer to a device private structure. + * @sw_context: Pointer to the software context. + * @header: A command header with an embedded user-space context handle. + * + * Convenience function: Call vmw_cmd_res_check with the user-space context + * handle embedded in @header. + */ +static int vmw_cmd_cid_check(struct vmw_private *dev_priv, +			     struct vmw_sw_context *sw_context, +			     SVGA3dCmdHeader *header) +{ +	struct vmw_cid_cmd { +		SVGA3dCmdHeader header; +		__le32 cid; +	} *cmd; + +	cmd = container_of(header, struct vmw_cid_cmd, header); +	return vmw_cmd_res_check(dev_priv, sw_context, vmw_res_context, +				 user_context_converter, &cmd->cid, NULL); +}  static int vmw_cmd_set_render_target_check(struct vmw_private *dev_priv,  					   struct vmw_sw_context *sw_context, @@ -194,7 +460,9 @@ static int vmw_cmd_set_render_target_check(struct vmw_private *dev_priv,  		return ret;  	cmd = container_of(header, struct vmw_sid_cmd, header); -	ret = vmw_cmd_sid_check(dev_priv, sw_context, &cmd->body.target.sid); +	ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, +				user_surface_converter, +				&cmd->body.target.sid, NULL);  	return ret;  } @@ -209,10 +477,14 @@ static int vmw_cmd_surface_copy_check(struct vmw_private *dev_priv,  	int ret;  	cmd = container_of(header, struct vmw_sid_cmd, header); -	ret = vmw_cmd_sid_check(dev_priv, sw_context, &cmd->body.src.sid); +	ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, +				user_surface_converter, +				&cmd->body.src.sid, NULL);  	if (unlikely(ret != 0))  		return ret; -	return vmw_cmd_sid_check(dev_priv, sw_context, &cmd->body.dest.sid); +	return vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, +				 user_surface_converter, +				 &cmd->body.dest.sid, NULL);  }  static int vmw_cmd_stretch_blt_check(struct vmw_private *dev_priv, @@ -226,10 +498,14 @@ static int vmw_cmd_stretch_blt_check(struct vmw_private *dev_priv,  	int ret;  	cmd = container_of(header, struct vmw_sid_cmd, header); -	ret = vmw_cmd_sid_check(dev_priv, sw_context, &cmd->body.src.sid); +	ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, +				user_surface_converter, +				&cmd->body.src.sid, NULL);  	if (unlikely(ret != 0))  		return ret; -	return vmw_cmd_sid_check(dev_priv, sw_context, &cmd->body.dest.sid); +	return vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, +				 user_surface_converter, +				 &cmd->body.dest.sid, NULL);  }  static int vmw_cmd_blt_surf_screen_check(struct vmw_private *dev_priv, @@ -248,7 +524,9 @@ static int vmw_cmd_blt_surf_screen_check(struct vmw_private *dev_priv,  		return -EPERM;  	} -	return vmw_cmd_sid_check(dev_priv, sw_context, &cmd->body.srcImage.sid); +	return vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, +				 user_surface_converter, +				 &cmd->body.srcImage.sid, NULL);  }  static int vmw_cmd_present_check(struct vmw_private *dev_priv, @@ -268,14 +546,15 @@ static int vmw_cmd_present_check(struct vmw_private *dev_priv,  		return -EPERM;  	} -	return vmw_cmd_sid_check(dev_priv, sw_context, &cmd->body.sid); +	return vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, +				 user_surface_converter, &cmd->body.sid, +				 NULL);  }  /**   * vmw_query_bo_switch_prepare - Prepare to switch pinned buffer for queries.   *   * @dev_priv: The device private structure. - * @cid: The hardware context for the next query.   * @new_query_bo: The new buffer holding query results.   * @sw_context: The software context used for this command submission.   * @@ -283,18 +562,18 @@ static int vmw_cmd_present_check(struct vmw_private *dev_priv,   * query results, and if another buffer currently is pinned for query   * results. If so, the function prepares the state of @sw_context for   * switching pinned buffers after successful submission of the current - * command batch. It also checks whether we're using a new query context. - * In that case, it makes sure we emit a query barrier for the old - * context before the current query buffer is fenced. + * command batch.   */  static int vmw_query_bo_switch_prepare(struct vmw_private *dev_priv, -				       uint32_t cid,  				       struct ttm_buffer_object *new_query_bo,  				       struct vmw_sw_context *sw_context)  { +	struct vmw_res_cache_entry *ctx_entry = +		&sw_context->res_cache[vmw_res_context];  	int ret; -	bool add_cid = false; -	uint32_t cid_to_add; + +	BUG_ON(!ctx_entry->valid); +	sw_context->last_query_ctx = ctx_entry->res;  	if (unlikely(new_query_bo != sw_context->cur_query_bo)) { @@ -304,9 +583,7 @@ static int vmw_query_bo_switch_prepare(struct vmw_private *dev_priv,  		}  		if (unlikely(sw_context->cur_query_bo != NULL)) { -			BUG_ON(!sw_context->query_cid_valid); -			add_cid = true; -			cid_to_add = sw_context->cur_query_cid; +			sw_context->needs_post_query_barrier = true;  			ret = vmw_bo_to_validate_list(sw_context,  						      sw_context->cur_query_bo,  						      NULL); @@ -323,27 +600,6 @@ static int vmw_query_bo_switch_prepare(struct vmw_private *dev_priv,  	} -	if (unlikely(cid != sw_context->cur_query_cid && -		     sw_context->query_cid_valid)) { -		add_cid = true; -		cid_to_add = sw_context->cur_query_cid; -	} - -	sw_context->cur_query_cid = cid; -	sw_context->query_cid_valid = true; - -	if (add_cid) { -		struct vmw_resource *ctx = sw_context->cur_ctx; - -		if (list_empty(&ctx->query_head)) -			list_add_tail(&ctx->query_head, -				      &sw_context->query_list); -		ret = vmw_bo_to_validate_list(sw_context, -					      dev_priv->dummy_query_bo, -					      NULL); -		if (unlikely(ret != 0)) -			return ret; -	}  	return 0;  } @@ -355,10 +611,9 @@ static int vmw_query_bo_switch_prepare(struct vmw_private *dev_priv,   * @sw_context: The software context used for this command submission batch.   *   * This function will check if we're switching query buffers, and will then, - * if no other query waits are issued this command submission batch,   * issue a dummy occlusion query wait used as a query barrier. When the fence   * object following that query wait has signaled, we are sure that all - * preseding queries have finished, and the old query buffer can be unpinned. + * preceding queries have finished, and the old query buffer can be unpinned.   * However, since both the new query buffer and the old one are fenced with   * that fence, we can do an asynchronus unpin now, and be sure that the   * old query buffer won't be moved until the fence has signaled. @@ -369,20 +624,19 @@ static int vmw_query_bo_switch_prepare(struct vmw_private *dev_priv,  static void vmw_query_bo_switch_commit(struct vmw_private *dev_priv,  				     struct vmw_sw_context *sw_context)  { - -	struct vmw_resource *ctx, *next_ctx; -	int ret; -  	/*  	 * The validate list should still hold references to all  	 * contexts here.  	 */ -	list_for_each_entry_safe(ctx, next_ctx, &sw_context->query_list, -				 query_head) { -		list_del_init(&ctx->query_head); +	if (sw_context->needs_post_query_barrier) { +		struct vmw_res_cache_entry *ctx_entry = +			&sw_context->res_cache[vmw_res_context]; +		struct vmw_resource *ctx; +		int ret; -		BUG_ON(list_empty(&ctx->validate_head)); +		BUG_ON(!ctx_entry->valid); +		ctx = ctx_entry->res;  		ret = vmw_fifo_emit_dummy_query(dev_priv, ctx->id); @@ -396,40 +650,46 @@ static void vmw_query_bo_switch_commit(struct vmw_private *dev_priv,  			ttm_bo_unref(&dev_priv->pinned_bo);  		} -		vmw_bo_pin(sw_context->cur_query_bo, true); +		if (!sw_context->needs_post_query_barrier) { +			vmw_bo_pin(sw_context->cur_query_bo, true); -		/* -		 * We pin also the dummy_query_bo buffer so that we -		 * don't need to validate it when emitting -		 * dummy queries in context destroy paths. -		 */ +			/* +			 * We pin also the dummy_query_bo buffer so that we +			 * don't need to validate it when emitting +			 * dummy queries in context destroy paths. +			 */ -		vmw_bo_pin(dev_priv->dummy_query_bo, true); -		dev_priv->dummy_query_bo_pinned = true; +			vmw_bo_pin(dev_priv->dummy_query_bo, true); +			dev_priv->dummy_query_bo_pinned = true; -		dev_priv->query_cid = sw_context->cur_query_cid; -		dev_priv->pinned_bo = -			ttm_bo_reference(sw_context->cur_query_bo); +			BUG_ON(sw_context->last_query_ctx == NULL); +			dev_priv->query_cid = sw_context->last_query_ctx->id; +			dev_priv->query_cid_valid = true; +			dev_priv->pinned_bo = +				ttm_bo_reference(sw_context->cur_query_bo); +		}  	}  }  /** - * vmw_query_switch_backoff - clear query barrier list - * @sw_context: The sw context used for this submission batch. + * vmw_translate_guest_pointer - Prepare to translate a user-space buffer + * handle to a valid SVGAGuestPtr   * - * This function is used as part of an error path, where a previously - * set up list of query barriers needs to be cleared. + * @dev_priv: Pointer to a device private structure. + * @sw_context: The software context used for this command batch validation. + * @ptr: Pointer to the user-space handle to be translated. + * @vmw_bo_p: Points to a location that, on successful return will carry + * a reference-counted pointer to the DMA buffer identified by the + * user-space handle in @id.   * + * This function saves information needed to translate a user-space buffer + * handle to a valid SVGAGuestPtr. The translation does not take place + * immediately, but during a call to vmw_apply_relocations(). + * This function builds a relocation list and a list of buffers to validate. + * The former needs to be freed using either vmw_apply_relocations() or + * vmw_free_relocations(). The latter needs to be freed using + * vmw_clear_validations.   */ -static void vmw_query_switch_backoff(struct vmw_sw_context *sw_context) -{ -	struct list_head *list, *next; - -	list_for_each_safe(list, next, &sw_context->query_list) { -		list_del_init(list); -	} -} -  static int vmw_translate_guest_ptr(struct vmw_private *dev_priv,  				   struct vmw_sw_context *sw_context,  				   SVGAGuestPtr *ptr, @@ -471,6 +731,37 @@ out_no_reloc:  	return ret;  } +/** + * vmw_cmd_begin_query - validate a  SVGA_3D_CMD_BEGIN_QUERY command. + * + * @dev_priv: Pointer to a device private struct. + * @sw_context: The software context used for this command submission. + * @header: Pointer to the command header in the command stream. + */ +static int vmw_cmd_begin_query(struct vmw_private *dev_priv, +			       struct vmw_sw_context *sw_context, +			       SVGA3dCmdHeader *header) +{ +	struct vmw_begin_query_cmd { +		SVGA3dCmdHeader header; +		SVGA3dCmdBeginQuery q; +	} *cmd; + +	cmd = container_of(header, struct vmw_begin_query_cmd, +			   header); + +	return vmw_cmd_res_check(dev_priv, sw_context, vmw_res_context, +				 user_context_converter, &cmd->q.cid, +				 NULL); +} + +/** + * vmw_cmd_end_query - validate a  SVGA_3D_CMD_END_QUERY command. + * + * @dev_priv: Pointer to a device private struct. + * @sw_context: The software context used for this command submission. + * @header: Pointer to the command header in the command stream. + */  static int vmw_cmd_end_query(struct vmw_private *dev_priv,  			     struct vmw_sw_context *sw_context,  			     SVGA3dCmdHeader *header) @@ -493,13 +784,19 @@ static int vmw_cmd_end_query(struct vmw_private *dev_priv,  	if (unlikely(ret != 0))  		return ret; -	ret = vmw_query_bo_switch_prepare(dev_priv, cmd->q.cid, -					  &vmw_bo->base, sw_context); +	ret = vmw_query_bo_switch_prepare(dev_priv, &vmw_bo->base, sw_context);  	vmw_dmabuf_unreference(&vmw_bo);  	return ret;  } +/* + * vmw_cmd_wait_query - validate a  SVGA_3D_CMD_WAIT_QUERY command. + * + * @dev_priv: Pointer to a device private struct. + * @sw_context: The software context used for this command submission. + * @header: Pointer to the command header in the command stream. + */  static int vmw_cmd_wait_query(struct vmw_private *dev_priv,  			      struct vmw_sw_context *sw_context,  			      SVGA3dCmdHeader *header) @@ -510,7 +807,6 @@ static int vmw_cmd_wait_query(struct vmw_private *dev_priv,  		SVGA3dCmdWaitForQuery q;  	} *cmd;  	int ret; -	struct vmw_resource *ctx;  	cmd = container_of(header, struct vmw_query_cmd, header);  	ret = vmw_cmd_cid_check(dev_priv, sw_context, header); @@ -524,16 +820,6 @@ static int vmw_cmd_wait_query(struct vmw_private *dev_priv,  		return ret;  	vmw_dmabuf_unreference(&vmw_bo); - -	/* -	 * This wait will act as a barrier for previous waits for this -	 * context. -	 */ - -	ctx = sw_context->cur_ctx; -	if (!list_empty(&ctx->query_head)) -		list_del_init(&ctx->query_head); -  	return 0;  } @@ -542,14 +828,12 @@ static int vmw_cmd_dma(struct vmw_private *dev_priv,  		       SVGA3dCmdHeader *header)  {  	struct vmw_dma_buffer *vmw_bo = NULL; -	struct ttm_buffer_object *bo;  	struct vmw_surface *srf = NULL;  	struct vmw_dma_cmd {  		SVGA3dCmdHeader header;  		SVGA3dCmdSurfaceDMA dma;  	} *cmd;  	int ret; -	struct vmw_resource *res;  	cmd = container_of(header, struct vmw_dma_cmd, header);  	ret = vmw_translate_guest_ptr(dev_priv, sw_context, @@ -558,37 +842,20 @@ static int vmw_cmd_dma(struct vmw_private *dev_priv,  	if (unlikely(ret != 0))  		return ret; -	bo = &vmw_bo->base; -	ret = vmw_user_surface_lookup_handle(dev_priv, sw_context->tfile, -					     cmd->dma.host.sid, &srf); -	if (ret) { -		DRM_ERROR("could not find surface\n"); -		goto out_no_reloc; -	} - -	ret = vmw_surface_validate(dev_priv, srf); +	ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, +				user_surface_converter, &cmd->dma.host.sid, +				NULL);  	if (unlikely(ret != 0)) { -		if (ret != -ERESTARTSYS) -			DRM_ERROR("Culd not validate surface.\n"); -		goto out_no_validate; +		if (unlikely(ret != -ERESTARTSYS)) +			DRM_ERROR("could not find surface for DMA.\n"); +		goto out_no_surface;  	} -	/* -	 * Patch command stream with device SID. -	 */ -	cmd->dma.host.sid = srf->res.id; -	vmw_kms_cursor_snoop(srf, sw_context->tfile, bo, header); - -	vmw_dmabuf_unreference(&vmw_bo); - -	res = &srf->res; -	vmw_resource_to_validate_list(sw_context, &res); +	srf = vmw_res_to_srf(sw_context->res_cache[vmw_res_surface].res); -	return 0; +	vmw_kms_cursor_snoop(srf, sw_context->tfile, &vmw_bo->base, header); -out_no_validate: -	vmw_surface_unreference(&srf); -out_no_reloc: +out_no_surface:  	vmw_dmabuf_unreference(&vmw_bo);  	return ret;  } @@ -621,8 +888,9 @@ static int vmw_cmd_draw(struct vmw_private *dev_priv,  	}  	for (i = 0; i < cmd->body.numVertexDecls; ++i, ++decl) { -		ret = vmw_cmd_sid_check(dev_priv, sw_context, -					&decl->array.surfaceId); +		ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, +					user_surface_converter, +					&decl->array.surfaceId, NULL);  		if (unlikely(ret != 0))  			return ret;  	} @@ -636,8 +904,9 @@ static int vmw_cmd_draw(struct vmw_private *dev_priv,  	range = (SVGA3dPrimitiveRange *) decl;  	for (i = 0; i < cmd->body.numRanges; ++i, ++range) { -		ret = vmw_cmd_sid_check(dev_priv, sw_context, -					&range->indexArray.surfaceId); +		ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, +					user_surface_converter, +					&range->indexArray.surfaceId, NULL);  		if (unlikely(ret != 0))  			return ret;  	} @@ -668,8 +937,9 @@ static int vmw_cmd_tex_state(struct vmw_private *dev_priv,  		if (likely(cur_state->name != SVGA3D_TS_BIND_TEXTURE))  			continue; -		ret = vmw_cmd_sid_check(dev_priv, sw_context, -					&cur_state->value); +		ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, +					user_surface_converter, +					&cur_state->value, NULL);  		if (unlikely(ret != 0))  			return ret;  	} @@ -700,6 +970,34 @@ static int vmw_cmd_check_define_gmrfb(struct vmw_private *dev_priv,  	return ret;  } +/** + * vmw_cmd_set_shader - Validate an SVGA_3D_CMD_SET_SHADER + * command + * + * @dev_priv: Pointer to a device private struct. + * @sw_context: The software context being used for this batch. + * @header: Pointer to the command header in the command stream. + */ +static int vmw_cmd_set_shader(struct vmw_private *dev_priv, +			      struct vmw_sw_context *sw_context, +			      SVGA3dCmdHeader *header) +{ +	struct vmw_set_shader_cmd { +		SVGA3dCmdHeader header; +		SVGA3dCmdSetShader body; +	} *cmd; +	int ret; + +	cmd = container_of(header, struct vmw_set_shader_cmd, +			   header); + +	ret = vmw_cmd_cid_check(dev_priv, sw_context, header); +	if (unlikely(ret != 0)) +		return ret; + +	return 0; +} +  static int vmw_cmd_check_not_3d(struct vmw_private *dev_priv,  				struct vmw_sw_context *sw_context,  				void *buf, uint32_t *size) @@ -773,16 +1071,20 @@ static vmw_cmd_func vmw_cmd_funcs[SVGA_3D_CMD_MAX] = {  	VMW_CMD_DEF(SVGA_3D_CMD_PRESENT, &vmw_cmd_present_check),  	VMW_CMD_DEF(SVGA_3D_CMD_SHADER_DEFINE, &vmw_cmd_cid_check),  	VMW_CMD_DEF(SVGA_3D_CMD_SHADER_DESTROY, &vmw_cmd_cid_check), -	VMW_CMD_DEF(SVGA_3D_CMD_SET_SHADER, &vmw_cmd_cid_check), +	VMW_CMD_DEF(SVGA_3D_CMD_SET_SHADER, &vmw_cmd_set_shader),  	VMW_CMD_DEF(SVGA_3D_CMD_SET_SHADER_CONST, &vmw_cmd_cid_check),  	VMW_CMD_DEF(SVGA_3D_CMD_DRAW_PRIMITIVES, &vmw_cmd_draw),  	VMW_CMD_DEF(SVGA_3D_CMD_SETSCISSORRECT, &vmw_cmd_cid_check), -	VMW_CMD_DEF(SVGA_3D_CMD_BEGIN_QUERY, &vmw_cmd_cid_check), +	VMW_CMD_DEF(SVGA_3D_CMD_BEGIN_QUERY, &vmw_cmd_begin_query),  	VMW_CMD_DEF(SVGA_3D_CMD_END_QUERY, &vmw_cmd_end_query),  	VMW_CMD_DEF(SVGA_3D_CMD_WAIT_FOR_QUERY, &vmw_cmd_wait_query),  	VMW_CMD_DEF(SVGA_3D_CMD_PRESENT_READBACK, &vmw_cmd_ok),  	VMW_CMD_DEF(SVGA_3D_CMD_BLIT_SURFACE_TO_SCREEN, -		    &vmw_cmd_blt_surf_screen_check) +		    &vmw_cmd_blt_surf_screen_check), +	VMW_CMD_DEF(SVGA_3D_CMD_SURFACE_DEFINE_V2, &vmw_cmd_invalid), +	VMW_CMD_DEF(SVGA_3D_CMD_GENERATE_MIPMAPS, &vmw_cmd_invalid), +	VMW_CMD_DEF(SVGA_3D_CMD_ACTIVATE_SURFACE, &vmw_cmd_invalid), +	VMW_CMD_DEF(SVGA_3D_CMD_DEACTIVATE_SURFACE, &vmw_cmd_invalid),  };  static int vmw_cmd_check(struct vmw_private *dev_priv, @@ -829,6 +1131,8 @@ static int vmw_cmd_check_all(struct vmw_private *dev_priv,  	int32_t cur_size = size;  	int ret; +	sw_context->buf_start = buf; +  	while (cur_size > 0) {  		size = cur_size;  		ret = vmw_cmd_check(dev_priv, sw_context, buf, &size); @@ -860,43 +1164,63 @@ static void vmw_apply_relocations(struct vmw_sw_context *sw_context)  	for (i = 0; i < sw_context->cur_reloc; ++i) {  		reloc = &sw_context->relocs[i]; -		validate = &sw_context->val_bufs[reloc->index]; +		validate = &sw_context->val_bufs[reloc->index].base;  		bo = validate->bo; -		if (bo->mem.mem_type == TTM_PL_VRAM) { +		switch (bo->mem.mem_type) { +		case TTM_PL_VRAM:  			reloc->location->offset += bo->offset;  			reloc->location->gmrId = SVGA_GMR_FRAMEBUFFER; -		} else +			break; +		case VMW_PL_GMR:  			reloc->location->gmrId = bo->mem.start; +			break; +		default: +			BUG(); +		}  	}  	vmw_free_relocations(sw_context);  } +/** + * vmw_resource_list_unrefererence - Free up a resource list and unreference + * all resources referenced by it. + * + * @list: The resource list. + */ +static void vmw_resource_list_unreference(struct list_head *list) +{ +	struct vmw_resource_val_node *val, *val_next; + +	/* +	 * Drop references to resources held during command submission. +	 */ + +	list_for_each_entry_safe(val, val_next, list, head) { +		list_del_init(&val->head); +		vmw_resource_unreference(&val->res); +		kfree(val); +	} +} +  static void vmw_clear_validations(struct vmw_sw_context *sw_context)  { -	struct ttm_validate_buffer *entry, *next; -	struct vmw_resource *res, *res_next; +	struct vmw_validate_buffer *entry, *next; +	struct vmw_resource_val_node *val;  	/*  	 * Drop references to DMA buffers held during command submission.  	 */  	list_for_each_entry_safe(entry, next, &sw_context->validate_nodes, -				 head) { -		list_del(&entry->head); -		vmw_dmabuf_validate_clear(entry->bo); -		ttm_bo_unref(&entry->bo); +				 base.head) { +		list_del(&entry->base.head); +		ttm_bo_unref(&entry->base.bo); +		(void) drm_ht_remove_item(&sw_context->res_ht, &entry->hash);  		sw_context->cur_val_buf--;  	}  	BUG_ON(sw_context->cur_val_buf != 0); -	/* -	 * Drop references to resources held during command submission. -	 */ -	vmw_resource_unreserve(&sw_context->resource_list); -	list_for_each_entry_safe(res, res_next, &sw_context->resource_list, -				 validate_head) { -		list_del_init(&res->validate_head); -		vmw_resource_unreference(&res); -	} +	list_for_each_entry(val, &sw_context->resource_list, head) +		(void) drm_ht_remove_item(&sw_context->res_ht, &val->hash);  }  static int vmw_validate_single_buffer(struct vmw_private *dev_priv, @@ -939,11 +1263,11 @@ static int vmw_validate_single_buffer(struct vmw_private *dev_priv,  static int vmw_validate_buffers(struct vmw_private *dev_priv,  				struct vmw_sw_context *sw_context)  { -	struct ttm_validate_buffer *entry; +	struct vmw_validate_buffer *entry;  	int ret; -	list_for_each_entry(entry, &sw_context->validate_nodes, head) { -		ret = vmw_validate_single_buffer(dev_priv, entry->bo); +	list_for_each_entry(entry, &sw_context->validate_nodes, base.head) { +		ret = vmw_validate_single_buffer(dev_priv, entry->base.bo);  		if (unlikely(ret != 0))  			return ret;  	} @@ -1106,6 +1430,8 @@ int vmw_execbuf_process(struct drm_file *file_priv,  {  	struct vmw_sw_context *sw_context = &dev_priv->ctx;  	struct vmw_fence_obj *fence = NULL; +	struct vmw_resource *error_resource; +	struct list_head resource_list;  	uint32_t handle;  	void *cmd;  	int ret; @@ -1135,24 +1461,33 @@ int vmw_execbuf_process(struct drm_file *file_priv,  		sw_context->kernel = true;  	sw_context->tfile = vmw_fpriv(file_priv)->tfile; -	sw_context->cid_valid = false; -	sw_context->sid_valid = false;  	sw_context->cur_reloc = 0;  	sw_context->cur_val_buf = 0;  	sw_context->fence_flags = 0; -	INIT_LIST_HEAD(&sw_context->query_list);  	INIT_LIST_HEAD(&sw_context->resource_list);  	sw_context->cur_query_bo = dev_priv->pinned_bo; -	sw_context->cur_query_cid = dev_priv->query_cid; -	sw_context->query_cid_valid = (dev_priv->pinned_bo != NULL); - +	sw_context->last_query_ctx = NULL; +	sw_context->needs_post_query_barrier = false; +	memset(sw_context->res_cache, 0, sizeof(sw_context->res_cache));  	INIT_LIST_HEAD(&sw_context->validate_nodes); +	INIT_LIST_HEAD(&sw_context->res_relocations); +	if (!sw_context->res_ht_initialized) { +		ret = drm_ht_create(&sw_context->res_ht, VMW_RES_HT_ORDER); +		if (unlikely(ret != 0)) +			goto out_unlock; +		sw_context->res_ht_initialized = true; +	} +	INIT_LIST_HEAD(&resource_list);  	ret = vmw_cmd_check_all(dev_priv, sw_context, kernel_commands,  				command_size);  	if (unlikely(ret != 0))  		goto out_err; +	ret = vmw_resources_reserve(sw_context); +	if (unlikely(ret != 0)) +		goto out_err; +  	ret = ttm_eu_reserve_buffers(&sw_context->validate_nodes);  	if (unlikely(ret != 0))  		goto out_err; @@ -1161,24 +1496,31 @@ int vmw_execbuf_process(struct drm_file *file_priv,  	if (unlikely(ret != 0))  		goto out_err; -	vmw_apply_relocations(sw_context); +	ret = vmw_resources_validate(sw_context); +	if (unlikely(ret != 0)) +		goto out_err;  	if (throttle_us) {  		ret = vmw_wait_lag(dev_priv, &dev_priv->fifo.marker_queue,  				   throttle_us);  		if (unlikely(ret != 0)) -			goto out_throttle; +			goto out_err;  	}  	cmd = vmw_fifo_reserve(dev_priv, command_size);  	if (unlikely(cmd == NULL)) {  		DRM_ERROR("Failed reserving fifo space for commands.\n");  		ret = -ENOMEM; -		goto out_throttle; +		goto out_err;  	} +	vmw_apply_relocations(sw_context);  	memcpy(cmd, kernel_commands, command_size); + +	vmw_resource_relocations_apply(cmd, &sw_context->res_relocations); +	vmw_resource_relocations_free(&sw_context->res_relocations); +  	vmw_fifo_commit(dev_priv, command_size);  	vmw_query_bo_switch_commit(dev_priv, sw_context); @@ -1194,9 +1536,14 @@ int vmw_execbuf_process(struct drm_file *file_priv,  	if (ret != 0)  		DRM_ERROR("Fence submission error. Syncing.\n"); +	vmw_resource_list_unreserve(&sw_context->resource_list, false);  	ttm_eu_fence_buffer_objects(&sw_context->validate_nodes,  				    (void *) fence); +	if (unlikely(dev_priv->pinned_bo != NULL && +		     !dev_priv->query_cid_valid)) +		__vmw_execbuf_release_pinned_bo(dev_priv, fence); +  	vmw_clear_validations(sw_context);  	vmw_execbuf_copy_fence_user(dev_priv, vmw_fpriv(file_priv), ret,  				    user_fence_rep, fence, handle); @@ -1209,17 +1556,40 @@ int vmw_execbuf_process(struct drm_file *file_priv,  		vmw_fence_obj_unreference(&fence);  	} +	list_splice_init(&sw_context->resource_list, &resource_list);  	mutex_unlock(&dev_priv->cmdbuf_mutex); + +	/* +	 * Unreference resources outside of the cmdbuf_mutex to +	 * avoid deadlocks in resource destruction paths. +	 */ +	vmw_resource_list_unreference(&resource_list); +  	return 0;  out_err: +	vmw_resource_relocations_free(&sw_context->res_relocations);  	vmw_free_relocations(sw_context); -out_throttle: -	vmw_query_switch_backoff(sw_context);  	ttm_eu_backoff_reservation(&sw_context->validate_nodes); +	vmw_resource_list_unreserve(&sw_context->resource_list, true);  	vmw_clear_validations(sw_context); +	if (unlikely(dev_priv->pinned_bo != NULL && +		     !dev_priv->query_cid_valid)) +		__vmw_execbuf_release_pinned_bo(dev_priv, NULL);  out_unlock: +	list_splice_init(&sw_context->resource_list, &resource_list); +	error_resource = sw_context->error_resource; +	sw_context->error_resource = NULL;  	mutex_unlock(&dev_priv->cmdbuf_mutex); + +	/* +	 * Unreference resources outside of the cmdbuf_mutex to +	 * avoid deadlocks in resource destruction paths. +	 */ +	vmw_resource_list_unreference(&resource_list); +	if (unlikely(error_resource != NULL)) +		vmw_resource_unreference(&error_resource); +  	return ret;  } @@ -1244,13 +1614,13 @@ static void vmw_execbuf_unpin_panic(struct vmw_private *dev_priv)  /** - * vmw_execbuf_release_pinned_bo - Flush queries and unpin the pinned + * __vmw_execbuf_release_pinned_bo - Flush queries and unpin the pinned   * query bo.   *   * @dev_priv: The device private structure. - * @only_on_cid_match: Only flush and unpin if the current active query cid - * matches @cid. - * @cid: Optional context id to match. + * @fence: If non-NULL should point to a struct vmw_fence_obj issued + * _after_ a query barrier that flushes all queries touching the current + * buffer pointed to by @dev_priv->pinned_bo   *   * This function should be used to unpin the pinned query bo, or   * as a query barrier when we need to make sure that all queries have @@ -1263,23 +1633,21 @@ static void vmw_execbuf_unpin_panic(struct vmw_private *dev_priv)   *   * The function will synchronize on the previous query barrier, and will   * thus not finish until that barrier has executed. + * + * the @dev_priv->cmdbuf_mutex needs to be held by the current thread + * before calling this function.   */ -void vmw_execbuf_release_pinned_bo(struct vmw_private *dev_priv, -				   bool only_on_cid_match, uint32_t cid) +void __vmw_execbuf_release_pinned_bo(struct vmw_private *dev_priv, +				     struct vmw_fence_obj *fence)  {  	int ret = 0;  	struct list_head validate_list;  	struct ttm_validate_buffer pinned_val, query_val; -	struct vmw_fence_obj *fence; - -	mutex_lock(&dev_priv->cmdbuf_mutex); +	struct vmw_fence_obj *lfence = NULL;  	if (dev_priv->pinned_bo == NULL)  		goto out_unlock; -	if (only_on_cid_match && cid != dev_priv->query_cid) -		goto out_unlock; -  	INIT_LIST_HEAD(&validate_list);  	pinned_val.bo = ttm_bo_reference(dev_priv->pinned_bo); @@ -1297,25 +1665,34 @@ void vmw_execbuf_release_pinned_bo(struct vmw_private *dev_priv,  		goto out_no_reserve;  	} -	ret = vmw_fifo_emit_dummy_query(dev_priv, dev_priv->query_cid); -	if (unlikely(ret != 0)) { -		vmw_execbuf_unpin_panic(dev_priv); -		goto out_no_emit; +	if (dev_priv->query_cid_valid) { +		BUG_ON(fence != NULL); +		ret = vmw_fifo_emit_dummy_query(dev_priv, dev_priv->query_cid); +		if (unlikely(ret != 0)) { +			vmw_execbuf_unpin_panic(dev_priv); +			goto out_no_emit; +		} +		dev_priv->query_cid_valid = false;  	}  	vmw_bo_pin(dev_priv->pinned_bo, false);  	vmw_bo_pin(dev_priv->dummy_query_bo, false);  	dev_priv->dummy_query_bo_pinned = false; -	(void) vmw_execbuf_fence_commands(NULL, dev_priv, &fence, NULL); +	if (fence == NULL) { +		(void) vmw_execbuf_fence_commands(NULL, dev_priv, &lfence, +						  NULL); +		fence = lfence; +	}  	ttm_eu_fence_buffer_objects(&validate_list, (void *) fence); +	if (lfence != NULL) +		vmw_fence_obj_unreference(&lfence);  	ttm_bo_unref(&query_val.bo);  	ttm_bo_unref(&pinned_val.bo);  	ttm_bo_unref(&dev_priv->pinned_bo);  out_unlock: -	mutex_unlock(&dev_priv->cmdbuf_mutex);  	return;  out_no_emit: @@ -1324,6 +1701,31 @@ out_no_reserve:  	ttm_bo_unref(&query_val.bo);  	ttm_bo_unref(&pinned_val.bo);  	ttm_bo_unref(&dev_priv->pinned_bo); +} + +/** + * vmw_execbuf_release_pinned_bo - Flush queries and unpin the pinned + * query bo. + * + * @dev_priv: The device private structure. + * + * This function should be used to unpin the pinned query bo, or + * as a query barrier when we need to make sure that all queries have + * finished before the next fifo command. (For example on hardware + * context destructions where the hardware may otherwise leak unfinished + * queries). + * + * This function does not return any failure codes, but make attempts + * to do safe unpinning in case of errors. + * + * The function will synchronize on the previous query barrier, and will + * thus not finish until that barrier has executed. + */ +void vmw_execbuf_release_pinned_bo(struct vmw_private *dev_priv) +{ +	mutex_lock(&dev_priv->cmdbuf_mutex); +	if (dev_priv->query_cid_valid) +		__vmw_execbuf_release_pinned_bo(dev_priv, NULL);  	mutex_unlock(&dev_priv->cmdbuf_mutex);  }  |