1,6 → 1,6 |
/************************************************************************** |
* |
* Copyright © 2009 VMware, Inc., Palo Alto, CA., USA |
* Copyright © 2009 - 2015 VMware, Inc., Palo Alto, CA., USA |
* All Rights Reserved. |
* |
* Permission is hereby granted, free of charge, to any person obtaining a |
29,6 → 29,8 |
#include "vmwgfx_reg.h" |
#include <drm/ttm/ttm_bo_api.h> |
#include <drm/ttm/ttm_placement.h> |
#include "vmwgfx_so.h" |
#include "vmwgfx_binding.h" |
|
#define VMW_RES_HT_ORDER 12 |
|
59,8 → 61,11 |
* @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. |
* @switching_backup: The command stream provides a new backup buffer for a |
* resource. |
* @no_buffer_needed: This means @switching_backup is true on first buffer |
* reference. So resource reservation does not need to allocate a backup |
* buffer for the resource. |
*/ |
struct vmw_resource_val_node { |
struct list_head head; |
69,8 → 74,9 |
struct vmw_dma_buffer *new_backup; |
struct vmw_ctx_binding_state *staged_bindings; |
unsigned long new_backup_offset; |
bool first_usage; |
bool no_buffer_needed; |
u32 first_usage : 1; |
u32 switching_backup : 1; |
u32 no_buffer_needed : 1; |
}; |
|
/** |
92,22 → 98,40 |
[(_cmd) - SVGA_3D_CMD_BASE] = {(_func), (_user_allow),\ |
(_gb_disable), (_gb_enable)} |
|
static int vmw_resource_context_res_add(struct vmw_private *dev_priv, |
struct vmw_sw_context *sw_context, |
struct vmw_resource *ctx); |
static int vmw_translate_mob_ptr(struct vmw_private *dev_priv, |
struct vmw_sw_context *sw_context, |
SVGAMobId *id, |
struct vmw_dma_buffer **vmw_bo_p); |
static int vmw_bo_to_validate_list(struct vmw_sw_context *sw_context, |
struct vmw_dma_buffer *vbo, |
bool validate_as_mob, |
uint32_t *p_val_node); |
|
|
/** |
* vmw_resource_unreserve - unreserve resources previously reserved for |
* vmw_resources_unreserve - unreserve resources previously reserved for |
* command submission. |
* |
* @list_head: list of resources to unreserve. |
* @sw_context: pointer to the software context |
* @backoff: Whether command submission failed. |
*/ |
static void vmw_resource_list_unreserve(struct list_head *list, |
static void vmw_resources_unreserve(struct vmw_sw_context *sw_context, |
bool backoff) |
{ |
struct vmw_resource_val_node *val; |
struct list_head *list = &sw_context->resource_list; |
|
if (sw_context->dx_query_mob && !backoff) |
vmw_context_bind_dx_query(sw_context->dx_query_ctx, |
sw_context->dx_query_mob); |
|
list_for_each_entry(val, list, head) { |
struct vmw_resource *res = val->res; |
struct vmw_dma_buffer *new_backup = |
backoff ? NULL : val->new_backup; |
bool switch_backup = |
(backoff) ? false : val->switching_backup; |
|
/* |
* Transfer staged context bindings to the |
115,19 → 139,72 |
*/ |
if (unlikely(val->staged_bindings)) { |
if (!backoff) { |
vmw_context_binding_state_transfer |
(val->res, val->staged_bindings); |
vmw_binding_state_commit |
(vmw_context_binding_state(val->res), |
val->staged_bindings); |
} |
kfree(val->staged_bindings); |
|
if (val->staged_bindings != sw_context->staged_bindings) |
vmw_binding_state_free(val->staged_bindings); |
else |
sw_context->staged_bindings_inuse = false; |
val->staged_bindings = NULL; |
} |
vmw_resource_unreserve(res, new_backup, |
vmw_resource_unreserve(res, switch_backup, val->new_backup, |
val->new_backup_offset); |
vmw_dmabuf_unreference(&val->new_backup); |
} |
} |
|
/** |
* vmw_cmd_ctx_first_setup - Perform the setup needed when a context is |
* added to the validate list. |
* |
* @dev_priv: Pointer to the device private: |
* @sw_context: The validation context: |
* @node: The validation node holding this context. |
*/ |
static int vmw_cmd_ctx_first_setup(struct vmw_private *dev_priv, |
struct vmw_sw_context *sw_context, |
struct vmw_resource_val_node *node) |
{ |
int ret; |
|
ret = vmw_resource_context_res_add(dev_priv, sw_context, node->res); |
if (unlikely(ret != 0)) |
goto out_err; |
|
if (!sw_context->staged_bindings) { |
sw_context->staged_bindings = |
vmw_binding_state_alloc(dev_priv); |
if (IS_ERR(sw_context->staged_bindings)) { |
DRM_ERROR("Failed to allocate context binding " |
"information.\n"); |
ret = PTR_ERR(sw_context->staged_bindings); |
sw_context->staged_bindings = NULL; |
goto out_err; |
} |
} |
|
if (sw_context->staged_bindings_inuse) { |
node->staged_bindings = vmw_binding_state_alloc(dev_priv); |
if (IS_ERR(node->staged_bindings)) { |
DRM_ERROR("Failed to allocate context binding " |
"information.\n"); |
ret = PTR_ERR(node->staged_bindings); |
node->staged_bindings = NULL; |
goto out_err; |
} |
} else { |
node->staged_bindings = sw_context->staged_bindings; |
sw_context->staged_bindings_inuse = true; |
} |
|
return 0; |
out_err: |
return ret; |
} |
|
/** |
* vmw_resource_val_add - Add a resource to the software context's |
* resource list if it's not already on it. |
141,6 → 218,7 |
struct vmw_resource *res, |
struct vmw_resource_val_node **p_node) |
{ |
struct vmw_private *dev_priv = res->dev_priv; |
struct vmw_resource_val_node *node; |
struct drm_hash_item *hash; |
int ret; |
169,17 → 247,93 |
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; |
|
if (!dev_priv->has_mob) { |
list_add_tail(&node->head, &sw_context->resource_list); |
return 0; |
} |
|
switch (vmw_res_type(res)) { |
case vmw_res_context: |
case vmw_res_dx_context: |
list_add(&node->head, &sw_context->ctx_resource_list); |
ret = vmw_cmd_ctx_first_setup(dev_priv, sw_context, node); |
break; |
case vmw_res_cotable: |
list_add_tail(&node->head, &sw_context->ctx_resource_list); |
break; |
default: |
list_add_tail(&node->head, &sw_context->resource_list); |
break; |
} |
|
return ret; |
} |
|
/** |
* vmw_view_res_val_add - Add a view and the surface it's pointing to |
* to the validation list |
* |
* @sw_context: The software context holding the validation list. |
* @view: Pointer to the view resource. |
* |
* Returns 0 if success, negative error code otherwise. |
*/ |
static int vmw_view_res_val_add(struct vmw_sw_context *sw_context, |
struct vmw_resource *view) |
{ |
int ret; |
|
/* |
* First add the resource the view is pointing to, otherwise |
* it may be swapped out when the view is validated. |
*/ |
ret = vmw_resource_val_add(sw_context, vmw_view_srf(view), NULL); |
if (ret) |
return ret; |
|
return vmw_resource_val_add(sw_context, view, NULL); |
} |
|
/** |
* vmw_view_id_val_add - Look up a view and add it and the surface it's |
* pointing to to the validation list. |
* |
* @sw_context: The software context holding the validation list. |
* @view_type: The view type to look up. |
* @id: view id of the view. |
* |
* The view is represented by a view id and the DX context it's created on, |
* or scheduled for creation on. If there is no DX context set, the function |
* will return -EINVAL. Otherwise returns 0 on success and -EINVAL on failure. |
*/ |
static int vmw_view_id_val_add(struct vmw_sw_context *sw_context, |
enum vmw_view_type view_type, u32 id) |
{ |
struct vmw_resource_val_node *ctx_node = sw_context->dx_ctx_node; |
struct vmw_resource *view; |
int ret; |
|
if (!ctx_node) { |
DRM_ERROR("DX Context not set.\n"); |
return -EINVAL; |
} |
|
view = vmw_view_lookup(sw_context->man, view_type, id); |
if (IS_ERR(view)) |
return PTR_ERR(view); |
|
ret = vmw_view_res_val_add(sw_context, view); |
vmw_resource_unreference(&view); |
|
return ret; |
} |
|
/** |
* vmw_resource_context_res_add - Put resources previously bound to a context on |
* the validation list |
* |
195,24 → 349,56 |
struct vmw_resource *ctx) |
{ |
struct list_head *binding_list; |
struct vmw_ctx_binding *entry; |
struct vmw_ctx_bindinfo *entry; |
int ret = 0; |
struct vmw_resource *res; |
u32 i; |
|
/* Add all cotables to the validation list. */ |
if (dev_priv->has_dx && vmw_res_type(ctx) == vmw_res_dx_context) { |
for (i = 0; i < SVGA_COTABLE_DX10_MAX; ++i) { |
res = vmw_context_cotable(ctx, i); |
if (IS_ERR(res)) |
continue; |
|
ret = vmw_resource_val_add(sw_context, res, NULL); |
vmw_resource_unreference(&res); |
if (unlikely(ret != 0)) |
return ret; |
} |
} |
|
|
/* Add all resources bound to the context to the validation list */ |
mutex_lock(&dev_priv->binding_mutex); |
binding_list = vmw_context_binding_list(ctx); |
|
list_for_each_entry(entry, binding_list, ctx_list) { |
res = vmw_resource_reference_unless_doomed(entry->bi.res); |
/* entry->res is not refcounted */ |
res = vmw_resource_reference_unless_doomed(entry->res); |
if (unlikely(res == NULL)) |
continue; |
|
ret = vmw_resource_val_add(sw_context, entry->bi.res, NULL); |
if (vmw_res_type(entry->res) == vmw_res_view) |
ret = vmw_view_res_val_add(sw_context, entry->res); |
else |
ret = vmw_resource_val_add(sw_context, entry->res, |
NULL); |
vmw_resource_unreference(&res); |
if (unlikely(ret != 0)) |
break; |
} |
|
if (dev_priv->has_dx && vmw_res_type(ctx) == vmw_res_dx_context) { |
struct vmw_dma_buffer *dx_query_mob; |
|
dx_query_mob = vmw_context_get_dx_query_mob(ctx); |
if (dx_query_mob) |
ret = vmw_bo_to_validate_list(sw_context, |
dx_query_mob, |
true, NULL); |
} |
|
mutex_unlock(&dev_priv->binding_mutex); |
return ret; |
} |
308,7 → 494,7 |
* submission is reached. |
*/ |
static int vmw_bo_to_validate_list(struct vmw_sw_context *sw_context, |
struct ttm_buffer_object *bo, |
struct vmw_dma_buffer *vbo, |
bool validate_as_mob, |
uint32_t *p_val_node) |
{ |
318,7 → 504,7 |
struct drm_hash_item *hash; |
int ret; |
|
if (likely(drm_ht_find_item(&sw_context->res_ht, (unsigned long) bo, |
if (likely(drm_ht_find_item(&sw_context->res_ht, (unsigned long) vbo, |
&hash) == 0)) { |
vval_buf = container_of(hash, struct vmw_validate_buffer, |
hash); |
336,7 → 522,7 |
return -EINVAL; |
} |
vval_buf = &sw_context->val_bufs[val_node]; |
vval_buf->hash.key = (unsigned long) bo; |
vval_buf->hash.key = (unsigned long) vbo; |
ret = drm_ht_insert_item(&sw_context->res_ht, &vval_buf->hash); |
if (unlikely(ret != 0)) { |
DRM_ERROR("Failed to initialize a buffer validation " |
345,14 → 531,12 |
} |
++sw_context->cur_val_buf; |
val_buf = &vval_buf->base; |
val_buf->bo = ttm_bo_reference(bo); |
val_buf->reserved = false; |
val_buf->bo = ttm_bo_reference(&vbo->base); |
val_buf->shared = false; |
list_add_tail(&val_buf->head, &sw_context->validate_nodes); |
vval_buf->validate_as_mob = validate_as_mob; |
} |
|
sw_context->fence_flags |= DRM_VMW_FENCE_FLAG_EXEC; |
|
if (p_val_node) |
*p_val_node = val_node; |
|
372,20 → 556,20 |
static int vmw_resources_reserve(struct vmw_sw_context *sw_context) |
{ |
struct vmw_resource_val_node *val; |
int ret; |
int ret = 0; |
|
list_for_each_entry(val, &sw_context->resource_list, head) { |
struct vmw_resource *res = val->res; |
|
ret = vmw_resource_reserve(res, val->no_buffer_needed); |
ret = vmw_resource_reserve(res, true, val->no_buffer_needed); |
if (unlikely(ret != 0)) |
return ret; |
|
if (res->backup) { |
struct ttm_buffer_object *bo = &res->backup->base; |
struct vmw_dma_buffer *vbo = res->backup; |
|
ret = vmw_bo_to_validate_list |
(sw_context, bo, |
(sw_context, vbo, |
vmw_resource_needs_backup(res), NULL); |
|
if (unlikely(ret != 0)) |
392,9 → 576,21 |
return ret; |
} |
} |
return 0; |
|
if (sw_context->dx_query_mob) { |
struct vmw_dma_buffer *expected_dx_query_mob; |
|
expected_dx_query_mob = |
vmw_context_get_dx_query_mob(sw_context->dx_query_ctx); |
if (expected_dx_query_mob && |
expected_dx_query_mob != sw_context->dx_query_mob) { |
ret = -EINVAL; |
} |
} |
|
return ret; |
} |
|
/** |
* vmw_resources_validate - Validate all resources on the sw_context's |
* resource list. |
411,6 → 607,7 |
|
list_for_each_entry(val, &sw_context->resource_list, head) { |
struct vmw_resource *res = val->res; |
struct vmw_dma_buffer *backup = res->backup; |
|
ret = vmw_resource_validate(res); |
if (unlikely(ret != 0)) { |
418,11 → 615,23 |
DRM_ERROR("Failed to validate resource.\n"); |
return ret; |
} |
|
/* Check if the resource switched backup buffer */ |
if (backup && res->backup && (backup != res->backup)) { |
struct vmw_dma_buffer *vbo = res->backup; |
|
ret = vmw_bo_to_validate_list |
(sw_context, vbo, |
vmw_resource_needs_backup(res), NULL); |
if (ret) { |
ttm_bo_unreserve(&vbo->base); |
return ret; |
} |
} |
} |
return 0; |
} |
|
|
/** |
* vmw_cmd_res_reloc_add - Add a resource to a software context's |
* relocation- and validation lists. |
429,7 → 638,6 |
* |
* @dev_priv: Pointer to a struct vmw_private identifying the device. |
* @sw_context: Pointer to the software context. |
* @res_type: Resource type. |
* @id_loc: Pointer to where the id that needs translation is located. |
* @res: Valid pointer to a struct vmw_resource. |
* @p_val: If non null, a pointer to the struct vmw_resource_validate_node |
437,7 → 645,6 |
*/ |
static int vmw_cmd_res_reloc_add(struct vmw_private *dev_priv, |
struct vmw_sw_context *sw_context, |
enum vmw_res_type res_type, |
uint32_t *id_loc, |
struct vmw_resource *res, |
struct vmw_resource_val_node **p_val) |
450,40 → 657,16 |
res, |
id_loc - sw_context->buf_start); |
if (unlikely(ret != 0)) |
goto out_err; |
return ret; |
|
ret = vmw_resource_val_add(sw_context, res, &node); |
if (unlikely(ret != 0)) |
goto out_err; |
return ret; |
|
if (res_type == vmw_res_context && dev_priv->has_mob && |
node->first_usage) { |
|
/* |
* Put contexts first on the list to be able to exit |
* list traversal for contexts early. |
*/ |
list_del(&node->head); |
list_add(&node->head, &sw_context->resource_list); |
|
ret = vmw_resource_context_res_add(dev_priv, sw_context, res); |
if (unlikely(ret != 0)) |
goto out_err; |
node->staged_bindings = |
kzalloc(sizeof(*node->staged_bindings), GFP_KERNEL); |
if (node->staged_bindings == NULL) { |
DRM_ERROR("Failed to allocate context binding " |
"information.\n"); |
goto out_err; |
} |
INIT_LIST_HEAD(&node->staged_bindings->list); |
} |
|
if (p_val) |
*p_val = node; |
|
out_err: |
return ret; |
return 0; |
} |
|
|
549,7 → 732,7 |
if (unlikely(ret != 0)) { |
DRM_ERROR("Could not find or use resource 0x%08x.\n", |
(unsigned) *id_loc); |
.. dump_stack(); |
// dump_stack(); |
return ret; |
} |
|
557,7 → 740,7 |
rcache->res = res; |
rcache->handle = *id_loc; |
|
ret = vmw_cmd_res_reloc_add(dev_priv, sw_context, res_type, id_loc, |
ret = vmw_cmd_res_reloc_add(dev_priv, sw_context, id_loc, |
res, &node); |
if (unlikely(ret != 0)) |
goto out_no_reloc; |
576,6 → 759,46 |
} |
|
/** |
* vmw_rebind_dx_query - Rebind DX query associated with the context |
* |
* @ctx_res: context the query belongs to |
* |
* This function assumes binding_mutex is held. |
*/ |
static int vmw_rebind_all_dx_query(struct vmw_resource *ctx_res) |
{ |
struct vmw_private *dev_priv = ctx_res->dev_priv; |
struct vmw_dma_buffer *dx_query_mob; |
struct { |
SVGA3dCmdHeader header; |
SVGA3dCmdDXBindAllQuery body; |
} *cmd; |
|
|
dx_query_mob = vmw_context_get_dx_query_mob(ctx_res); |
|
if (!dx_query_mob || dx_query_mob->dx_query_ctx) |
return 0; |
|
cmd = vmw_fifo_reserve_dx(dev_priv, sizeof(*cmd), ctx_res->id); |
|
if (cmd == NULL) { |
DRM_ERROR("Failed to rebind queries.\n"); |
return -ENOMEM; |
} |
|
cmd->header.id = SVGA_3D_CMD_DX_BIND_ALL_QUERY; |
cmd->header.size = sizeof(cmd->body); |
cmd->body.cid = ctx_res->id; |
cmd->body.mobid = dx_query_mob->base.mem.start; |
vmw_fifo_commit(dev_priv, sizeof(*cmd)); |
|
vmw_context_bind_dx_query(ctx_res, dx_query_mob); |
|
return 0; |
} |
|
/** |
* vmw_rebind_contexts - Rebind all resources previously bound to |
* referenced contexts. |
* |
592,12 → 815,17 |
if (unlikely(!val->staged_bindings)) |
break; |
|
ret = vmw_context_rebind_all(val->res); |
ret = vmw_binding_rebind_all |
(vmw_context_binding_state(val->res)); |
if (unlikely(ret != 0)) { |
if (ret != -ERESTARTSYS) |
DRM_ERROR("Failed to rebind context.\n"); |
return ret; |
} |
|
ret = vmw_rebind_all_dx_query(val->res); |
if (ret != 0) |
return ret; |
} |
|
return 0; |
604,6 → 832,69 |
} |
|
/** |
* vmw_view_bindings_add - Add an array of view bindings to a context |
* binding state tracker. |
* |
* @sw_context: The execbuf state used for this command. |
* @view_type: View type for the bindings. |
* @binding_type: Binding type for the bindings. |
* @shader_slot: The shader slot to user for the bindings. |
* @view_ids: Array of view ids to be bound. |
* @num_views: Number of view ids in @view_ids. |
* @first_slot: The binding slot to be used for the first view id in @view_ids. |
*/ |
static int vmw_view_bindings_add(struct vmw_sw_context *sw_context, |
enum vmw_view_type view_type, |
enum vmw_ctx_binding_type binding_type, |
uint32 shader_slot, |
uint32 view_ids[], u32 num_views, |
u32 first_slot) |
{ |
struct vmw_resource_val_node *ctx_node = sw_context->dx_ctx_node; |
struct vmw_cmdbuf_res_manager *man; |
u32 i; |
int ret; |
|
if (!ctx_node) { |
DRM_ERROR("DX Context not set.\n"); |
return -EINVAL; |
} |
|
man = sw_context->man; |
for (i = 0; i < num_views; ++i) { |
struct vmw_ctx_bindinfo_view binding; |
struct vmw_resource *view = NULL; |
|
if (view_ids[i] != SVGA3D_INVALID_ID) { |
view = vmw_view_lookup(man, view_type, view_ids[i]); |
if (IS_ERR(view)) { |
DRM_ERROR("View not found.\n"); |
return PTR_ERR(view); |
} |
|
ret = vmw_view_res_val_add(sw_context, view); |
if (ret) { |
DRM_ERROR("Could not add view to " |
"validation list.\n"); |
vmw_resource_unreference(&view); |
return ret; |
} |
} |
binding.bi.ctx = ctx_node->res; |
binding.bi.res = view; |
binding.bi.bt = binding_type; |
binding.shader_slot = shader_slot; |
binding.slot = first_slot + i; |
vmw_binding_add(ctx_node->staged_bindings, &binding.bi, |
shader_slot, binding.slot); |
if (view) |
vmw_resource_unreference(&view); |
} |
|
return 0; |
} |
|
/** |
* vmw_cmd_cid_check - Check a command header for valid context information. |
* |
* @dev_priv: Pointer to a device private structure. |
641,6 → 932,12 |
|
cmd = container_of(header, struct vmw_sid_cmd, header); |
|
if (cmd->body.type >= SVGA3D_RT_MAX) { |
DRM_ERROR("Illegal render target type %u.\n", |
(unsigned) cmd->body.type); |
return -EINVAL; |
} |
|
ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_context, |
user_context_converter, &cmd->body.cid, |
&ctx_node); |
654,13 → 951,14 |
return ret; |
|
if (dev_priv->has_mob) { |
struct vmw_ctx_bindinfo bi; |
struct vmw_ctx_bindinfo_view binding; |
|
bi.ctx = ctx_node->res; |
bi.res = res_node ? res_node->res : NULL; |
bi.bt = vmw_ctx_binding_rt; |
bi.i1.rt_type = cmd->body.type; |
return vmw_context_binding_add(ctx_node->staged_bindings, &bi); |
binding.bi.ctx = ctx_node->res; |
binding.bi.res = res_node ? res_node->res : NULL; |
binding.bi.bt = vmw_ctx_binding_rt; |
binding.slot = cmd->body.type; |
vmw_binding_add(ctx_node->staged_bindings, |
&binding.bi, 0, binding.slot); |
} |
|
return 0; |
677,16 → 975,62 |
int ret; |
|
cmd = container_of(header, struct vmw_sid_cmd, header); |
|
ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, |
user_surface_converter, |
&cmd->body.src.sid, NULL); |
if (unlikely(ret != 0)) |
if (ret) |
return ret; |
|
return vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, |
user_surface_converter, |
&cmd->body.dest.sid, NULL); |
} |
|
static int vmw_cmd_buffer_copy_check(struct vmw_private *dev_priv, |
struct vmw_sw_context *sw_context, |
SVGA3dCmdHeader *header) |
{ |
struct { |
SVGA3dCmdHeader header; |
SVGA3dCmdDXBufferCopy body; |
} *cmd; |
int ret; |
|
cmd = container_of(header, typeof(*cmd), header); |
ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, |
user_surface_converter, |
&cmd->body.src, NULL); |
if (ret != 0) |
return ret; |
|
return vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, |
user_surface_converter, |
&cmd->body.dest, NULL); |
} |
|
static int vmw_cmd_pred_copy_check(struct vmw_private *dev_priv, |
struct vmw_sw_context *sw_context, |
SVGA3dCmdHeader *header) |
{ |
struct { |
SVGA3dCmdHeader header; |
SVGA3dCmdDXPredCopyRegion body; |
} *cmd; |
int ret; |
|
cmd = container_of(header, typeof(*cmd), header); |
ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, |
user_surface_converter, |
&cmd->body.srcSid, NULL); |
if (ret != 0) |
return ret; |
|
return vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, |
user_surface_converter, |
&cmd->body.dstSid, NULL); |
} |
|
static int vmw_cmd_stretch_blt_check(struct vmw_private *dev_priv, |
struct vmw_sw_context *sw_context, |
SVGA3dCmdHeader *header) |
755,7 → 1099,7 |
* command batch. |
*/ |
static int vmw_query_bo_switch_prepare(struct vmw_private *dev_priv, |
struct ttm_buffer_object *new_query_bo, |
struct vmw_dma_buffer *new_query_bo, |
struct vmw_sw_context *sw_context) |
{ |
struct vmw_res_cache_entry *ctx_entry = |
767,7 → 1111,7 |
|
if (unlikely(new_query_bo != sw_context->cur_query_bo)) { |
|
if (unlikely(new_query_bo->num_pages > 4)) { |
if (unlikely(new_query_bo->base.num_pages > 4)) { |
DRM_ERROR("Query buffer too large.\n"); |
return -EINVAL; |
} |
836,12 → 1180,12 |
|
if (dev_priv->pinned_bo != sw_context->cur_query_bo) { |
if (dev_priv->pinned_bo) { |
vmw_bo_pin(dev_priv->pinned_bo, false); |
ttm_bo_unref(&dev_priv->pinned_bo); |
vmw_bo_pin_reserved(dev_priv->pinned_bo, false); |
vmw_dmabuf_unreference(&dev_priv->pinned_bo); |
} |
|
if (!sw_context->needs_post_query_barrier) { |
vmw_bo_pin(sw_context->cur_query_bo, true); |
vmw_bo_pin_reserved(sw_context->cur_query_bo, true); |
|
/* |
* We pin also the dummy_query_bo buffer so that we |
849,14 → 1193,17 |
* dummy queries in context destroy paths. |
*/ |
|
vmw_bo_pin(dev_priv->dummy_query_bo, true); |
if (!dev_priv->dummy_query_bo_pinned) { |
vmw_bo_pin_reserved(dev_priv->dummy_query_bo, |
true); |
dev_priv->dummy_query_bo_pinned = true; |
} |
|
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_dmabuf_reference(sw_context->cur_query_bo); |
} |
} |
} |
885,17 → 1232,17 |
struct vmw_dma_buffer **vmw_bo_p) |
{ |
struct vmw_dma_buffer *vmw_bo = NULL; |
struct ttm_buffer_object *bo; |
uint32_t handle = *id; |
struct vmw_relocation *reloc; |
int ret; |
|
ret = vmw_user_dmabuf_lookup(sw_context->fp->tfile, handle, &vmw_bo); |
ret = vmw_user_dmabuf_lookup(sw_context->fp->tfile, handle, &vmw_bo, |
NULL); |
if (unlikely(ret != 0)) { |
DRM_ERROR("Could not find or use MOB buffer.\n"); |
return -EINVAL; |
ret = -EINVAL; |
goto out_no_reloc; |
} |
bo = &vmw_bo->base; |
|
if (unlikely(sw_context->cur_reloc >= VMWGFX_MAX_RELOCATIONS)) { |
DRM_ERROR("Max number relocations per submission" |
908,7 → 1255,7 |
reloc->mob_loc = id; |
reloc->location = NULL; |
|
ret = vmw_bo_to_validate_list(sw_context, bo, true, &reloc->index); |
ret = vmw_bo_to_validate_list(sw_context, vmw_bo, true, &reloc->index); |
if (unlikely(ret != 0)) |
goto out_no_reloc; |
|
917,7 → 1264,7 |
|
out_no_reloc: |
vmw_dmabuf_unreference(&vmw_bo); |
vmw_bo_p = NULL; |
*vmw_bo_p = NULL; |
return ret; |
} |
|
946,17 → 1293,17 |
struct vmw_dma_buffer **vmw_bo_p) |
{ |
struct vmw_dma_buffer *vmw_bo = NULL; |
struct ttm_buffer_object *bo; |
uint32_t handle = ptr->gmrId; |
struct vmw_relocation *reloc; |
int ret; |
|
ret = vmw_user_dmabuf_lookup(sw_context->fp->tfile, handle, &vmw_bo); |
ret = vmw_user_dmabuf_lookup(sw_context->fp->tfile, handle, &vmw_bo, |
NULL); |
if (unlikely(ret != 0)) { |
DRM_ERROR("Could not find or use GMR region.\n"); |
return -EINVAL; |
ret = -EINVAL; |
goto out_no_reloc; |
} |
bo = &vmw_bo->base; |
|
if (unlikely(sw_context->cur_reloc >= VMWGFX_MAX_RELOCATIONS)) { |
DRM_ERROR("Max number relocations per submission" |
968,7 → 1315,7 |
reloc = &sw_context->relocs[sw_context->cur_reloc++]; |
reloc->location = ptr; |
|
ret = vmw_bo_to_validate_list(sw_context, bo, false, &reloc->index); |
ret = vmw_bo_to_validate_list(sw_context, vmw_bo, false, &reloc->index); |
if (unlikely(ret != 0)) |
goto out_no_reloc; |
|
977,11 → 1324,103 |
|
out_no_reloc: |
vmw_dmabuf_unreference(&vmw_bo); |
vmw_bo_p = NULL; |
*vmw_bo_p = NULL; |
return ret; |
} |
|
|
|
/** |
* vmw_cmd_dx_define_query - validate a SVGA_3D_CMD_DX_DEFINE_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. |
* |
* This function adds the new query into the query COTABLE |
*/ |
static int vmw_cmd_dx_define_query(struct vmw_private *dev_priv, |
struct vmw_sw_context *sw_context, |
SVGA3dCmdHeader *header) |
{ |
struct vmw_dx_define_query_cmd { |
SVGA3dCmdHeader header; |
SVGA3dCmdDXDefineQuery q; |
} *cmd; |
|
int ret; |
struct vmw_resource_val_node *ctx_node = sw_context->dx_ctx_node; |
struct vmw_resource *cotable_res; |
|
|
if (ctx_node == NULL) { |
DRM_ERROR("DX Context not set for query.\n"); |
return -EINVAL; |
} |
|
cmd = container_of(header, struct vmw_dx_define_query_cmd, header); |
|
if (cmd->q.type < SVGA3D_QUERYTYPE_MIN || |
cmd->q.type >= SVGA3D_QUERYTYPE_MAX) |
return -EINVAL; |
|
cotable_res = vmw_context_cotable(ctx_node->res, SVGA_COTABLE_DXQUERY); |
ret = vmw_cotable_notify(cotable_res, cmd->q.queryId); |
vmw_resource_unreference(&cotable_res); |
|
return ret; |
} |
|
|
|
/** |
* vmw_cmd_dx_bind_query - validate a SVGA_3D_CMD_DX_BIND_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. |
* |
* The query bind operation will eventually associate the query ID |
* with its backing MOB. In this function, we take the user mode |
* MOB ID and use vmw_translate_mob_ptr() to translate it to its |
* kernel mode equivalent. |
*/ |
static int vmw_cmd_dx_bind_query(struct vmw_private *dev_priv, |
struct vmw_sw_context *sw_context, |
SVGA3dCmdHeader *header) |
{ |
struct vmw_dx_bind_query_cmd { |
SVGA3dCmdHeader header; |
SVGA3dCmdDXBindQuery q; |
} *cmd; |
|
struct vmw_dma_buffer *vmw_bo; |
int ret; |
|
|
cmd = container_of(header, struct vmw_dx_bind_query_cmd, header); |
|
/* |
* Look up the buffer pointed to by q.mobid, put it on the relocation |
* list so its kernel mode MOB ID can be filled in later |
*/ |
ret = vmw_translate_mob_ptr(dev_priv, sw_context, &cmd->q.mobid, |
&vmw_bo); |
|
if (ret != 0) |
return ret; |
|
sw_context->dx_query_mob = vmw_bo; |
sw_context->dx_query_ctx = sw_context->dx_ctx_node->res; |
|
vmw_dmabuf_unreference(&vmw_bo); |
|
return ret; |
} |
|
|
|
/** |
* vmw_cmd_begin_gb_query - validate a SVGA_3D_CMD_BEGIN_GB_QUERY command. |
* |
* @dev_priv: Pointer to a device private struct. |
1075,7 → 1514,7 |
if (unlikely(ret != 0)) |
return ret; |
|
ret = vmw_query_bo_switch_prepare(dev_priv, &vmw_bo->base, sw_context); |
ret = vmw_query_bo_switch_prepare(dev_priv, vmw_bo, sw_context); |
|
vmw_dmabuf_unreference(&vmw_bo); |
return ret; |
1129,7 → 1568,7 |
if (unlikely(ret != 0)) |
return ret; |
|
ret = vmw_query_bo_switch_prepare(dev_priv, &vmw_bo->base, sw_context); |
ret = vmw_query_bo_switch_prepare(dev_priv, vmw_bo, sw_context); |
|
vmw_dmabuf_unreference(&vmw_bo); |
return ret; |
1363,6 → 1802,12 |
if (likely(cur_state->name != SVGA3D_TS_BIND_TEXTURE)) |
continue; |
|
if (cur_state->stage >= SVGA3D_NUM_TEXTURE_UNITS) { |
DRM_ERROR("Illegal texture/sampler unit %u.\n", |
(unsigned) cur_state->stage); |
return -EINVAL; |
} |
|
ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, |
user_surface_converter, |
&cur_state->value, &res_node); |
1370,14 → 1815,14 |
return ret; |
|
if (dev_priv->has_mob) { |
struct vmw_ctx_bindinfo bi; |
struct vmw_ctx_bindinfo_tex binding; |
|
bi.ctx = ctx_node->res; |
bi.res = res_node ? res_node->res : NULL; |
bi.bt = vmw_ctx_binding_tex; |
bi.i1.texture_stage = cur_state->stage; |
vmw_context_binding_add(ctx_node->staged_bindings, |
&bi); |
binding.bi.ctx = ctx_node->res; |
binding.bi.res = res_node ? res_node->res : NULL; |
binding.bi.bt = vmw_ctx_binding_tex; |
binding.texture_stage = cur_state->stage; |
vmw_binding_add(ctx_node->staged_bindings, &binding.bi, |
0, binding.texture_stage); |
} |
} |
|
1407,7 → 1852,48 |
return ret; |
} |
|
|
/** |
* vmw_cmd_res_switch_backup - Utility function to handle backup buffer |
* switching |
* |
* @dev_priv: Pointer to a device private struct. |
* @sw_context: The software context being used for this batch. |
* @val_node: The validation node representing the resource. |
* @buf_id: Pointer to the user-space backup buffer handle in the command |
* stream. |
* @backup_offset: Offset of backup into MOB. |
* |
* This function prepares for registering a switch of backup buffers |
* in the resource metadata just prior to unreserving. It's basically a wrapper |
* around vmw_cmd_res_switch_backup with a different interface. |
*/ |
static int vmw_cmd_res_switch_backup(struct vmw_private *dev_priv, |
struct vmw_sw_context *sw_context, |
struct vmw_resource_val_node *val_node, |
uint32_t *buf_id, |
unsigned long backup_offset) |
{ |
struct vmw_dma_buffer *dma_buf; |
int ret; |
|
ret = vmw_translate_mob_ptr(dev_priv, sw_context, buf_id, &dma_buf); |
if (ret) |
return ret; |
|
val_node->switching_backup = true; |
if (val_node->first_usage) |
val_node->no_buffer_needed = true; |
|
vmw_dmabuf_unreference(&val_node->new_backup); |
val_node->new_backup = dma_buf; |
val_node->new_backup_offset = backup_offset; |
|
return 0; |
} |
|
|
/** |
* vmw_cmd_switch_backup - Utility function to handle backup buffer switching |
* |
* @dev_priv: Pointer to a device private struct. |
1420,7 → 1906,8 |
* @backup_offset: Offset of backup into MOB. |
* |
* This function prepares for registering a switch of backup buffers |
* in the resource metadata just prior to unreserving. |
* in the resource metadata just prior to unreserving. It's basically a wrapper |
* around vmw_cmd_res_switch_backup with a different interface. |
*/ |
static int vmw_cmd_switch_backup(struct vmw_private *dev_priv, |
struct vmw_sw_context *sw_context, |
1431,27 → 1918,16 |
uint32_t *buf_id, |
unsigned long backup_offset) |
{ |
struct vmw_resource_val_node *val_node; |
int ret; |
struct vmw_dma_buffer *dma_buf; |
struct vmw_resource_val_node *val_node; |
|
ret = vmw_cmd_res_check(dev_priv, sw_context, res_type, |
converter, res_id, &val_node); |
if (unlikely(ret != 0)) |
if (ret) |
return ret; |
|
ret = vmw_translate_mob_ptr(dev_priv, sw_context, buf_id, &dma_buf); |
if (unlikely(ret != 0)) |
return ret; |
|
if (val_node->first_usage) |
val_node->no_buffer_needed = true; |
|
vmw_dmabuf_unreference(&val_node->new_backup); |
val_node->new_backup = dma_buf; |
val_node->new_backup_offset = backup_offset; |
|
return 0; |
return vmw_cmd_res_switch_backup(dev_priv, sw_context, val_node, |
buf_id, backup_offset); |
} |
|
/** |
1623,8 → 2099,101 |
&cmd->body.sid, NULL); |
} |
|
#if 0 |
|
/** |
* vmw_cmd_shader_define - Validate an SVGA_3D_CMD_SHADER_DEFINE |
* 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_shader_define(struct vmw_private *dev_priv, |
struct vmw_sw_context *sw_context, |
SVGA3dCmdHeader *header) |
{ |
struct vmw_shader_define_cmd { |
SVGA3dCmdHeader header; |
SVGA3dCmdDefineShader body; |
} *cmd; |
int ret; |
size_t size; |
struct vmw_resource_val_node *val; |
|
cmd = container_of(header, struct vmw_shader_define_cmd, |
header); |
|
ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_context, |
user_context_converter, &cmd->body.cid, |
&val); |
if (unlikely(ret != 0)) |
return ret; |
|
if (unlikely(!dev_priv->has_mob)) |
return 0; |
|
size = cmd->header.size - sizeof(cmd->body); |
ret = vmw_compat_shader_add(dev_priv, |
vmw_context_res_man(val->res), |
cmd->body.shid, cmd + 1, |
cmd->body.type, size, |
&sw_context->staged_cmd_res); |
if (unlikely(ret != 0)) |
return ret; |
|
return vmw_resource_relocation_add(&sw_context->res_relocations, |
NULL, &cmd->header.id - |
sw_context->buf_start); |
|
return 0; |
} |
|
/** |
* vmw_cmd_shader_destroy - Validate an SVGA_3D_CMD_SHADER_DESTROY |
* 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_shader_destroy(struct vmw_private *dev_priv, |
struct vmw_sw_context *sw_context, |
SVGA3dCmdHeader *header) |
{ |
struct vmw_shader_destroy_cmd { |
SVGA3dCmdHeader header; |
SVGA3dCmdDestroyShader body; |
} *cmd; |
int ret; |
struct vmw_resource_val_node *val; |
|
cmd = container_of(header, struct vmw_shader_destroy_cmd, |
header); |
|
ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_context, |
user_context_converter, &cmd->body.cid, |
&val); |
if (unlikely(ret != 0)) |
return ret; |
|
if (unlikely(!dev_priv->has_mob)) |
return 0; |
|
ret = vmw_shader_remove(vmw_context_res_man(val->res), |
cmd->body.shid, |
cmd->body.type, |
&sw_context->staged_cmd_res); |
if (unlikely(ret != 0)) |
return ret; |
|
return vmw_resource_relocation_add(&sw_context->res_relocations, |
NULL, &cmd->header.id - |
sw_context->buf_start); |
|
return 0; |
} |
|
/** |
* vmw_cmd_set_shader - Validate an SVGA_3D_CMD_SET_SHADER |
* command |
* |
1641,7 → 2210,7 |
SVGA3dCmdSetShader body; |
} *cmd; |
struct vmw_resource_val_node *ctx_node, *res_node = NULL; |
struct vmw_ctx_bindinfo bi; |
struct vmw_ctx_bindinfo_shader binding; |
struct vmw_resource *res = NULL; |
int ret; |
|
1648,6 → 2217,12 |
cmd = container_of(header, struct vmw_set_shader_cmd, |
header); |
|
if (cmd->body.type >= SVGA3D_SHADERTYPE_PREDX_MAX) { |
DRM_ERROR("Illegal shader type %u.\n", |
(unsigned) cmd->body.type); |
return -EINVAL; |
} |
|
ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_context, |
user_context_converter, &cmd->body.cid, |
&ctx_node); |
1658,14 → 2233,12 |
return 0; |
|
if (cmd->body.shid != SVGA3D_INVALID_ID) { |
res = vmw_compat_shader_lookup |
(vmw_context_res_man(ctx_node->res), |
res = vmw_shader_lookup(vmw_context_res_man(ctx_node->res), |
cmd->body.shid, |
cmd->body.type); |
|
if (!IS_ERR(res)) { |
ret = vmw_cmd_res_reloc_add(dev_priv, sw_context, |
vmw_res_shader, |
&cmd->body.shid, res, |
&res_node); |
vmw_resource_unreference(&res); |
1683,13 → 2256,14 |
return ret; |
} |
|
bi.ctx = ctx_node->res; |
bi.res = res_node ? res_node->res : NULL; |
bi.bt = vmw_ctx_binding_shader; |
bi.i1.shader_type = cmd->body.type; |
return vmw_context_binding_add(ctx_node->staged_bindings, &bi); |
binding.bi.ctx = ctx_node->res; |
binding.bi.res = res_node ? res_node->res : NULL; |
binding.bi.bt = vmw_ctx_binding_shader; |
binding.shader_slot = cmd->body.type - SVGA3D_SHADERTYPE_MIN; |
vmw_binding_add(ctx_node->staged_bindings, &binding.bi, |
binding.shader_slot, 0); |
return 0; |
} |
#endif |
|
/** |
* vmw_cmd_set_shader_const - Validate an SVGA_3D_CMD_SET_SHADER_CONST |
1724,7 → 2298,6 |
return 0; |
} |
|
#if 0 |
/** |
* vmw_cmd_bind_gb_shader - Validate an SVGA_3D_CMD_BIND_GB_SHADER |
* command |
1750,8 → 2323,691 |
&cmd->body.shid, &cmd->body.mobid, |
cmd->body.offsetInBytes); |
} |
#endif |
|
/** |
* vmw_cmd_dx_set_single_constant_buffer - Validate an |
* SVGA_3D_CMD_DX_SET_SINGLE_CONSTANT_BUFFER 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_dx_set_single_constant_buffer(struct vmw_private *dev_priv, |
struct vmw_sw_context *sw_context, |
SVGA3dCmdHeader *header) |
{ |
struct { |
SVGA3dCmdHeader header; |
SVGA3dCmdDXSetSingleConstantBuffer body; |
} *cmd; |
struct vmw_resource_val_node *res_node = NULL; |
struct vmw_resource_val_node *ctx_node = sw_context->dx_ctx_node; |
struct vmw_ctx_bindinfo_cb binding; |
int ret; |
|
if (unlikely(ctx_node == NULL)) { |
DRM_ERROR("DX Context not set.\n"); |
return -EINVAL; |
} |
|
cmd = container_of(header, typeof(*cmd), header); |
ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, |
user_surface_converter, |
&cmd->body.sid, &res_node); |
if (unlikely(ret != 0)) |
return ret; |
|
binding.bi.ctx = ctx_node->res; |
binding.bi.res = res_node ? res_node->res : NULL; |
binding.bi.bt = vmw_ctx_binding_cb; |
binding.shader_slot = cmd->body.type - SVGA3D_SHADERTYPE_MIN; |
binding.offset = cmd->body.offsetInBytes; |
binding.size = cmd->body.sizeInBytes; |
binding.slot = cmd->body.slot; |
|
if (binding.shader_slot >= SVGA3D_NUM_SHADERTYPE_DX10 || |
binding.slot >= SVGA3D_DX_MAX_CONSTBUFFERS) { |
DRM_ERROR("Illegal const buffer shader %u slot %u.\n", |
(unsigned) cmd->body.type, |
(unsigned) binding.slot); |
return -EINVAL; |
} |
|
vmw_binding_add(ctx_node->staged_bindings, &binding.bi, |
binding.shader_slot, binding.slot); |
|
return 0; |
} |
|
/** |
* vmw_cmd_dx_set_shader_res - Validate an |
* SVGA_3D_CMD_DX_SET_SHADER_RESOURCES 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_dx_set_shader_res(struct vmw_private *dev_priv, |
struct vmw_sw_context *sw_context, |
SVGA3dCmdHeader *header) |
{ |
struct { |
SVGA3dCmdHeader header; |
SVGA3dCmdDXSetShaderResources body; |
} *cmd = container_of(header, typeof(*cmd), header); |
u32 num_sr_view = (cmd->header.size - sizeof(cmd->body)) / |
sizeof(SVGA3dShaderResourceViewId); |
|
if ((u64) cmd->body.startView + (u64) num_sr_view > |
(u64) SVGA3D_DX_MAX_SRVIEWS || |
cmd->body.type >= SVGA3D_SHADERTYPE_DX10_MAX) { |
DRM_ERROR("Invalid shader binding.\n"); |
return -EINVAL; |
} |
|
return vmw_view_bindings_add(sw_context, vmw_view_sr, |
vmw_ctx_binding_sr, |
cmd->body.type - SVGA3D_SHADERTYPE_MIN, |
(void *) &cmd[1], num_sr_view, |
cmd->body.startView); |
} |
|
/** |
* vmw_cmd_dx_set_shader - Validate an SVGA_3D_CMD_DX_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_dx_set_shader(struct vmw_private *dev_priv, |
struct vmw_sw_context *sw_context, |
SVGA3dCmdHeader *header) |
{ |
struct { |
SVGA3dCmdHeader header; |
SVGA3dCmdDXSetShader body; |
} *cmd; |
struct vmw_resource *res = NULL; |
struct vmw_resource_val_node *ctx_node = sw_context->dx_ctx_node; |
struct vmw_ctx_bindinfo_shader binding; |
int ret = 0; |
|
if (unlikely(ctx_node == NULL)) { |
DRM_ERROR("DX Context not set.\n"); |
return -EINVAL; |
} |
|
cmd = container_of(header, typeof(*cmd), header); |
|
if (cmd->body.type >= SVGA3D_SHADERTYPE_DX10_MAX) { |
DRM_ERROR("Illegal shader type %u.\n", |
(unsigned) cmd->body.type); |
return -EINVAL; |
} |
|
if (cmd->body.shaderId != SVGA3D_INVALID_ID) { |
res = vmw_shader_lookup(sw_context->man, cmd->body.shaderId, 0); |
if (IS_ERR(res)) { |
DRM_ERROR("Could not find shader for binding.\n"); |
return PTR_ERR(res); |
} |
|
ret = vmw_resource_val_add(sw_context, res, NULL); |
if (ret) |
goto out_unref; |
} |
|
binding.bi.ctx = ctx_node->res; |
binding.bi.res = res; |
binding.bi.bt = vmw_ctx_binding_dx_shader; |
binding.shader_slot = cmd->body.type - SVGA3D_SHADERTYPE_MIN; |
|
vmw_binding_add(ctx_node->staged_bindings, &binding.bi, |
binding.shader_slot, 0); |
out_unref: |
if (res) |
vmw_resource_unreference(&res); |
|
return ret; |
} |
|
/** |
* vmw_cmd_dx_set_vertex_buffers - Validates an |
* SVGA_3D_CMD_DX_SET_VERTEX_BUFFERS 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_dx_set_vertex_buffers(struct vmw_private *dev_priv, |
struct vmw_sw_context *sw_context, |
SVGA3dCmdHeader *header) |
{ |
struct vmw_resource_val_node *ctx_node = sw_context->dx_ctx_node; |
struct vmw_ctx_bindinfo_vb binding; |
struct vmw_resource_val_node *res_node; |
struct { |
SVGA3dCmdHeader header; |
SVGA3dCmdDXSetVertexBuffers body; |
SVGA3dVertexBuffer buf[]; |
} *cmd; |
int i, ret, num; |
|
if (unlikely(ctx_node == NULL)) { |
DRM_ERROR("DX Context not set.\n"); |
return -EINVAL; |
} |
|
cmd = container_of(header, typeof(*cmd), header); |
num = (cmd->header.size - sizeof(cmd->body)) / |
sizeof(SVGA3dVertexBuffer); |
if ((u64)num + (u64)cmd->body.startBuffer > |
(u64)SVGA3D_DX_MAX_VERTEXBUFFERS) { |
DRM_ERROR("Invalid number of vertex buffers.\n"); |
return -EINVAL; |
} |
|
for (i = 0; i < num; i++) { |
ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, |
user_surface_converter, |
&cmd->buf[i].sid, &res_node); |
if (unlikely(ret != 0)) |
return ret; |
|
binding.bi.ctx = ctx_node->res; |
binding.bi.bt = vmw_ctx_binding_vb; |
binding.bi.res = ((res_node) ? res_node->res : NULL); |
binding.offset = cmd->buf[i].offset; |
binding.stride = cmd->buf[i].stride; |
binding.slot = i + cmd->body.startBuffer; |
|
vmw_binding_add(ctx_node->staged_bindings, &binding.bi, |
0, binding.slot); |
} |
|
return 0; |
} |
|
/** |
* vmw_cmd_dx_ia_set_vertex_buffers - Validate an |
* SVGA_3D_CMD_DX_IA_SET_VERTEX_BUFFERS 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_dx_set_index_buffer(struct vmw_private *dev_priv, |
struct vmw_sw_context *sw_context, |
SVGA3dCmdHeader *header) |
{ |
struct vmw_resource_val_node *ctx_node = sw_context->dx_ctx_node; |
struct vmw_ctx_bindinfo_ib binding; |
struct vmw_resource_val_node *res_node; |
struct { |
SVGA3dCmdHeader header; |
SVGA3dCmdDXSetIndexBuffer body; |
} *cmd; |
int ret; |
|
if (unlikely(ctx_node == NULL)) { |
DRM_ERROR("DX Context not set.\n"); |
return -EINVAL; |
} |
|
cmd = container_of(header, typeof(*cmd), header); |
ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, |
user_surface_converter, |
&cmd->body.sid, &res_node); |
if (unlikely(ret != 0)) |
return ret; |
|
binding.bi.ctx = ctx_node->res; |
binding.bi.res = ((res_node) ? res_node->res : NULL); |
binding.bi.bt = vmw_ctx_binding_ib; |
binding.offset = cmd->body.offset; |
binding.format = cmd->body.format; |
|
vmw_binding_add(ctx_node->staged_bindings, &binding.bi, 0, 0); |
|
return 0; |
} |
|
/** |
* vmw_cmd_dx_set_rendertarget - Validate an |
* SVGA_3D_CMD_DX_SET_RENDERTARGETS 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_dx_set_rendertargets(struct vmw_private *dev_priv, |
struct vmw_sw_context *sw_context, |
SVGA3dCmdHeader *header) |
{ |
struct { |
SVGA3dCmdHeader header; |
SVGA3dCmdDXSetRenderTargets body; |
} *cmd = container_of(header, typeof(*cmd), header); |
int ret; |
u32 num_rt_view = (cmd->header.size - sizeof(cmd->body)) / |
sizeof(SVGA3dRenderTargetViewId); |
|
if (num_rt_view > SVGA3D_MAX_SIMULTANEOUS_RENDER_TARGETS) { |
DRM_ERROR("Invalid DX Rendertarget binding.\n"); |
return -EINVAL; |
} |
|
ret = vmw_view_bindings_add(sw_context, vmw_view_ds, |
vmw_ctx_binding_ds, 0, |
&cmd->body.depthStencilViewId, 1, 0); |
if (ret) |
return ret; |
|
return vmw_view_bindings_add(sw_context, vmw_view_rt, |
vmw_ctx_binding_dx_rt, 0, |
(void *)&cmd[1], num_rt_view, 0); |
} |
|
/** |
* vmw_cmd_dx_clear_rendertarget_view - Validate an |
* SVGA_3D_CMD_DX_CLEAR_RENDERTARGET_VIEW 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_dx_clear_rendertarget_view(struct vmw_private *dev_priv, |
struct vmw_sw_context *sw_context, |
SVGA3dCmdHeader *header) |
{ |
struct { |
SVGA3dCmdHeader header; |
SVGA3dCmdDXClearRenderTargetView body; |
} *cmd = container_of(header, typeof(*cmd), header); |
|
return vmw_view_id_val_add(sw_context, vmw_view_rt, |
cmd->body.renderTargetViewId); |
} |
|
/** |
* vmw_cmd_dx_clear_rendertarget_view - Validate an |
* SVGA_3D_CMD_DX_CLEAR_DEPTHSTENCIL_VIEW 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_dx_clear_depthstencil_view(struct vmw_private *dev_priv, |
struct vmw_sw_context *sw_context, |
SVGA3dCmdHeader *header) |
{ |
struct { |
SVGA3dCmdHeader header; |
SVGA3dCmdDXClearDepthStencilView body; |
} *cmd = container_of(header, typeof(*cmd), header); |
|
return vmw_view_id_val_add(sw_context, vmw_view_ds, |
cmd->body.depthStencilViewId); |
} |
|
static int vmw_cmd_dx_view_define(struct vmw_private *dev_priv, |
struct vmw_sw_context *sw_context, |
SVGA3dCmdHeader *header) |
{ |
struct vmw_resource_val_node *ctx_node = sw_context->dx_ctx_node; |
struct vmw_resource_val_node *srf_node; |
struct vmw_resource *res; |
enum vmw_view_type view_type; |
int ret; |
/* |
* This is based on the fact that all affected define commands have |
* the same initial command body layout. |
*/ |
struct { |
SVGA3dCmdHeader header; |
uint32 defined_id; |
uint32 sid; |
} *cmd; |
|
if (unlikely(ctx_node == NULL)) { |
DRM_ERROR("DX Context not set.\n"); |
return -EINVAL; |
} |
|
view_type = vmw_view_cmd_to_type(header->id); |
cmd = container_of(header, typeof(*cmd), header); |
ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, |
user_surface_converter, |
&cmd->sid, &srf_node); |
if (unlikely(ret != 0)) |
return ret; |
|
res = vmw_context_cotable(ctx_node->res, vmw_view_cotables[view_type]); |
ret = vmw_cotable_notify(res, cmd->defined_id); |
vmw_resource_unreference(&res); |
if (unlikely(ret != 0)) |
return ret; |
|
return vmw_view_add(sw_context->man, |
ctx_node->res, |
srf_node->res, |
view_type, |
cmd->defined_id, |
header, |
header->size + sizeof(*header), |
&sw_context->staged_cmd_res); |
} |
|
/** |
* vmw_cmd_dx_set_so_targets - Validate an |
* SVGA_3D_CMD_DX_SET_SOTARGETS 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_dx_set_so_targets(struct vmw_private *dev_priv, |
struct vmw_sw_context *sw_context, |
SVGA3dCmdHeader *header) |
{ |
struct vmw_resource_val_node *ctx_node = sw_context->dx_ctx_node; |
struct vmw_ctx_bindinfo_so binding; |
struct vmw_resource_val_node *res_node; |
struct { |
SVGA3dCmdHeader header; |
SVGA3dCmdDXSetSOTargets body; |
SVGA3dSoTarget targets[]; |
} *cmd; |
int i, ret, num; |
|
if (unlikely(ctx_node == NULL)) { |
DRM_ERROR("DX Context not set.\n"); |
return -EINVAL; |
} |
|
cmd = container_of(header, typeof(*cmd), header); |
num = (cmd->header.size - sizeof(cmd->body)) / |
sizeof(SVGA3dSoTarget); |
|
if (num > SVGA3D_DX_MAX_SOTARGETS) { |
DRM_ERROR("Invalid DX SO binding.\n"); |
return -EINVAL; |
} |
|
for (i = 0; i < num; i++) { |
ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, |
user_surface_converter, |
&cmd->targets[i].sid, &res_node); |
if (unlikely(ret != 0)) |
return ret; |
|
binding.bi.ctx = ctx_node->res; |
binding.bi.res = ((res_node) ? res_node->res : NULL); |
binding.bi.bt = vmw_ctx_binding_so, |
binding.offset = cmd->targets[i].offset; |
binding.size = cmd->targets[i].sizeInBytes; |
binding.slot = i; |
|
vmw_binding_add(ctx_node->staged_bindings, &binding.bi, |
0, binding.slot); |
} |
|
return 0; |
} |
|
static int vmw_cmd_dx_so_define(struct vmw_private *dev_priv, |
struct vmw_sw_context *sw_context, |
SVGA3dCmdHeader *header) |
{ |
struct vmw_resource_val_node *ctx_node = sw_context->dx_ctx_node; |
struct vmw_resource *res; |
/* |
* This is based on the fact that all affected define commands have |
* the same initial command body layout. |
*/ |
struct { |
SVGA3dCmdHeader header; |
uint32 defined_id; |
} *cmd; |
enum vmw_so_type so_type; |
int ret; |
|
if (unlikely(ctx_node == NULL)) { |
DRM_ERROR("DX Context not set.\n"); |
return -EINVAL; |
} |
|
so_type = vmw_so_cmd_to_type(header->id); |
res = vmw_context_cotable(ctx_node->res, vmw_so_cotables[so_type]); |
cmd = container_of(header, typeof(*cmd), header); |
ret = vmw_cotable_notify(res, cmd->defined_id); |
vmw_resource_unreference(&res); |
|
return ret; |
} |
|
/** |
* vmw_cmd_dx_check_subresource - Validate an |
* SVGA_3D_CMD_DX_[X]_SUBRESOURCE 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_dx_check_subresource(struct vmw_private *dev_priv, |
struct vmw_sw_context *sw_context, |
SVGA3dCmdHeader *header) |
{ |
struct { |
SVGA3dCmdHeader header; |
union { |
SVGA3dCmdDXReadbackSubResource r_body; |
SVGA3dCmdDXInvalidateSubResource i_body; |
SVGA3dCmdDXUpdateSubResource u_body; |
SVGA3dSurfaceId sid; |
}; |
} *cmd; |
|
BUILD_BUG_ON(offsetof(typeof(*cmd), r_body.sid) != |
offsetof(typeof(*cmd), sid)); |
BUILD_BUG_ON(offsetof(typeof(*cmd), i_body.sid) != |
offsetof(typeof(*cmd), sid)); |
BUILD_BUG_ON(offsetof(typeof(*cmd), u_body.sid) != |
offsetof(typeof(*cmd), sid)); |
|
cmd = container_of(header, typeof(*cmd), header); |
|
return vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, |
user_surface_converter, |
&cmd->sid, NULL); |
} |
|
static int vmw_cmd_dx_cid_check(struct vmw_private *dev_priv, |
struct vmw_sw_context *sw_context, |
SVGA3dCmdHeader *header) |
{ |
struct vmw_resource_val_node *ctx_node = sw_context->dx_ctx_node; |
|
if (unlikely(ctx_node == NULL)) { |
DRM_ERROR("DX Context not set.\n"); |
return -EINVAL; |
} |
|
return 0; |
} |
|
/** |
* vmw_cmd_dx_view_remove - validate a view remove command and |
* schedule the view resource for removal. |
* |
* @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. |
* |
* Check that the view exists, and if it was not created using this |
* command batch, make sure it's validated (present in the device) so that |
* the remove command will not confuse the device. |
*/ |
static int vmw_cmd_dx_view_remove(struct vmw_private *dev_priv, |
struct vmw_sw_context *sw_context, |
SVGA3dCmdHeader *header) |
{ |
struct vmw_resource_val_node *ctx_node = sw_context->dx_ctx_node; |
struct { |
SVGA3dCmdHeader header; |
union vmw_view_destroy body; |
} *cmd = container_of(header, typeof(*cmd), header); |
enum vmw_view_type view_type = vmw_view_cmd_to_type(header->id); |
struct vmw_resource *view; |
int ret; |
|
if (!ctx_node) { |
DRM_ERROR("DX Context not set.\n"); |
return -EINVAL; |
} |
|
ret = vmw_view_remove(sw_context->man, |
cmd->body.view_id, view_type, |
&sw_context->staged_cmd_res, |
&view); |
if (ret || !view) |
return ret; |
|
/* |
* Add view to the validate list iff it was not created using this |
* command batch. |
*/ |
return vmw_view_res_val_add(sw_context, view); |
} |
|
/** |
* vmw_cmd_dx_define_shader - Validate an SVGA_3D_CMD_DX_DEFINE_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_dx_define_shader(struct vmw_private *dev_priv, |
struct vmw_sw_context *sw_context, |
SVGA3dCmdHeader *header) |
{ |
struct vmw_resource_val_node *ctx_node = sw_context->dx_ctx_node; |
struct vmw_resource *res; |
struct { |
SVGA3dCmdHeader header; |
SVGA3dCmdDXDefineShader body; |
} *cmd = container_of(header, typeof(*cmd), header); |
int ret; |
|
if (!ctx_node) { |
DRM_ERROR("DX Context not set.\n"); |
return -EINVAL; |
} |
|
res = vmw_context_cotable(ctx_node->res, SVGA_COTABLE_DXSHADER); |
ret = vmw_cotable_notify(res, cmd->body.shaderId); |
vmw_resource_unreference(&res); |
if (ret) |
return ret; |
|
return vmw_dx_shader_add(sw_context->man, ctx_node->res, |
cmd->body.shaderId, cmd->body.type, |
&sw_context->staged_cmd_res); |
} |
|
/** |
* vmw_cmd_dx_destroy_shader - Validate an SVGA_3D_CMD_DX_DESTROY_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_dx_destroy_shader(struct vmw_private *dev_priv, |
struct vmw_sw_context *sw_context, |
SVGA3dCmdHeader *header) |
{ |
struct vmw_resource_val_node *ctx_node = sw_context->dx_ctx_node; |
struct { |
SVGA3dCmdHeader header; |
SVGA3dCmdDXDestroyShader body; |
} *cmd = container_of(header, typeof(*cmd), header); |
int ret; |
|
if (!ctx_node) { |
DRM_ERROR("DX Context not set.\n"); |
return -EINVAL; |
} |
|
ret = vmw_shader_remove(sw_context->man, cmd->body.shaderId, 0, |
&sw_context->staged_cmd_res); |
if (ret) |
DRM_ERROR("Could not find shader to remove.\n"); |
|
return ret; |
} |
|
/** |
* vmw_cmd_dx_bind_shader - Validate an SVGA_3D_CMD_DX_BIND_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_dx_bind_shader(struct vmw_private *dev_priv, |
struct vmw_sw_context *sw_context, |
SVGA3dCmdHeader *header) |
{ |
struct vmw_resource_val_node *ctx_node; |
struct vmw_resource_val_node *res_node; |
struct vmw_resource *res; |
struct { |
SVGA3dCmdHeader header; |
SVGA3dCmdDXBindShader body; |
} *cmd = container_of(header, typeof(*cmd), header); |
int ret; |
|
if (cmd->body.cid != SVGA3D_INVALID_ID) { |
ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_context, |
user_context_converter, |
&cmd->body.cid, &ctx_node); |
if (ret) |
return ret; |
} else { |
ctx_node = sw_context->dx_ctx_node; |
if (!ctx_node) { |
DRM_ERROR("DX Context not set.\n"); |
return -EINVAL; |
} |
} |
|
res = vmw_shader_lookup(vmw_context_res_man(ctx_node->res), |
cmd->body.shid, 0); |
if (IS_ERR(res)) { |
DRM_ERROR("Could not find shader to bind.\n"); |
return PTR_ERR(res); |
} |
|
ret = vmw_resource_val_add(sw_context, res, &res_node); |
if (ret) { |
DRM_ERROR("Error creating resource validation node.\n"); |
goto out_unref; |
} |
|
|
ret = vmw_cmd_res_switch_backup(dev_priv, sw_context, res_node, |
&cmd->body.mobid, |
cmd->body.offsetInBytes); |
out_unref: |
vmw_resource_unreference(&res); |
|
return ret; |
} |
|
static int vmw_cmd_check_not_3d(struct vmw_private *dev_priv, |
struct vmw_sw_context *sw_context, |
void *buf, uint32_t *size) |
1759,7 → 3015,7 |
uint32_t size_remaining = *size; |
uint32_t cmd_id; |
|
cmd_id = le32_to_cpu(((uint32_t *)buf)[0]); |
cmd_id = ((uint32_t *)buf)[0]; |
switch (cmd_id) { |
case SVGA_CMD_UPDATE: |
*size = sizeof(uint32_t) + sizeof(SVGAFifoCmdUpdate); |
1890,7 → 3146,7 |
false, false, true), |
VMW_CMD_DEF(SVGA_3D_CMD_DESTROY_GB_MOB, &vmw_cmd_invalid, |
false, false, true), |
VMW_CMD_DEF(SVGA_3D_CMD_REDEFINE_GB_MOB, &vmw_cmd_invalid, |
VMW_CMD_DEF(SVGA_3D_CMD_REDEFINE_GB_MOB64, &vmw_cmd_invalid, |
false, false, true), |
VMW_CMD_DEF(SVGA_3D_CMD_UPDATE_GB_MOB_MAPPING, &vmw_cmd_invalid, |
false, false, true), |
1975,14 → 3231,14 |
const struct vmw_cmd_entry *entry; |
bool gb = dev_priv->capabilities & SVGA_CAP_GBOBJECTS; |
|
cmd_id = le32_to_cpu(((uint32_t *)buf)[0]); |
cmd_id = ((uint32_t *)buf)[0]; |
/* Handle any none 3D commands */ |
if (unlikely(cmd_id < SVGA_CMD_MAX)) |
return vmw_cmd_check_not_3d(dev_priv, sw_context, buf, size); |
|
|
cmd_id = le32_to_cpu(header->id); |
*size = le32_to_cpu(header->size) + sizeof(SVGA3dCmdHeader); |
cmd_id = header->id; |
*size = header->size + sizeof(SVGA3dCmdHeader); |
|
cmd_id -= SVGA_3D_CMD_BASE; |
if (unlikely(*size > size_remaining)) |
2094,7 → 3350,8 |
* |
* @list: The resource list. |
*/ |
static void vmw_resource_list_unreference(struct list_head *list) |
static void vmw_resource_list_unreference(struct vmw_sw_context *sw_context, |
struct list_head *list) |
{ |
struct vmw_resource_val_node *val, *val_next; |
|
2105,8 → 3362,15 |
list_for_each_entry_safe(val, val_next, list, head) { |
list_del_init(&val->head); |
vmw_resource_unreference(&val->res); |
if (unlikely(val->staged_bindings)) |
kfree(val->staged_bindings); |
|
if (val->staged_bindings) { |
if (val->staged_bindings != sw_context->staged_bindings) |
vmw_binding_state_free(val->staged_bindings); |
else |
sw_context->staged_bindings_inuse = false; |
val->staged_bindings = NULL; |
} |
|
kfree(val); |
} |
} |
2132,24 → 3396,21 |
(void) drm_ht_remove_item(&sw_context->res_ht, &val->hash); |
} |
|
static int vmw_validate_single_buffer(struct vmw_private *dev_priv, |
int vmw_validate_single_buffer(struct vmw_private *dev_priv, |
struct ttm_buffer_object *bo, |
bool interruptible, |
bool validate_as_mob) |
{ |
struct vmw_dma_buffer *vbo = container_of(bo, struct vmw_dma_buffer, |
base); |
int ret; |
|
|
/* |
* Don't validate pinned buffers. |
*/ |
|
if (bo == dev_priv->pinned_bo || |
(bo == dev_priv->dummy_query_bo && |
dev_priv->dummy_query_bo_pinned)) |
if (vbo->pin_count > 0) |
return 0; |
|
if (validate_as_mob) |
return ttm_bo_validate(bo, &vmw_mob_placement, true, false); |
return ttm_bo_validate(bo, &vmw_mob_placement, interruptible, |
false); |
|
/** |
* Put BO in VRAM if there is space, otherwise as a GMR. |
2158,7 → 3419,8 |
* used as a GMR, this will return -ENOMEM. |
*/ |
|
ret = ttm_bo_validate(bo, &vmw_vram_gmr_placement, true, false); |
ret = ttm_bo_validate(bo, &vmw_vram_gmr_placement, interruptible, |
false); |
if (likely(ret == 0 || ret == -ERESTARTSYS)) |
return ret; |
|
2167,8 → 3429,7 |
* previous contents. |
*/ |
|
DRM_INFO("Falling through to VRAM.\n"); |
ret = ttm_bo_validate(bo, &vmw_vram_placement, true, false); |
ret = ttm_bo_validate(bo, &vmw_vram_placement, interruptible, false); |
return ret; |
} |
|
2180,6 → 3441,7 |
|
list_for_each_entry(entry, &sw_context->validate_nodes, base.head) { |
ret = vmw_validate_single_buffer(dev_priv, entry->base.bo, |
true, |
entry->validate_as_mob); |
if (unlikely(ret != 0)) |
return ret; |
2247,13 → 3509,9 |
|
if (p_handle != NULL) |
ret = vmw_user_fence_create(file_priv, dev_priv->fman, |
sequence, |
DRM_VMW_FENCE_FLAG_EXEC, |
p_fence, p_handle); |
sequence, p_fence, p_handle); |
else |
ret = vmw_fence_create(dev_priv->fman, sequence, |
DRM_VMW_FENCE_FLAG_EXEC, |
p_fence); |
ret = vmw_fence_create(dev_priv->fman, sequence, p_fence); |
|
if (unlikely(ret != 0 && !synced)) { |
(void) vmw_fallback_wait(dev_priv, false, false, |
2305,7 → 3563,7 |
BUG_ON(fence == NULL); |
|
fence_rep.handle = fence_handle; |
fence_rep.seqno = fence->seqno; |
fence_rep.seqno = fence->base.seqno; |
vmw_update_seqno(dev_priv, &dev_priv->fifo); |
fence_rep.passed_seqno = dev_priv->last_read_seqno; |
} |
2315,8 → 3573,8 |
* seeing fence_rep::error filled in. Typically |
* user-space would have pre-set that member to -EFAULT. |
*/ |
// ret = copy_to_user(user_fence_rep, &fence_rep, |
// sizeof(fence_rep)); |
ret = copy_to_user(user_fence_rep, &fence_rep, |
sizeof(fence_rep)); |
|
/* |
* User-space lost the fence object. We need to sync |
2326,14 → 3584,170 |
ttm_ref_object_base_unref(vmw_fp->tfile, |
fence_handle, TTM_REF_USAGE); |
DRM_ERROR("Fence copy error. Syncing.\n"); |
(void) vmw_fence_obj_wait(fence, fence->signal_mask, |
false, false, |
(void) vmw_fence_obj_wait(fence, false, false, |
VMW_FENCE_WAIT_TIMEOUT); |
} |
} |
|
/** |
* vmw_execbuf_submit_fifo - Patch a command batch and submit it using |
* the fifo. |
* |
* @dev_priv: Pointer to a device private structure. |
* @kernel_commands: Pointer to the unpatched command batch. |
* @command_size: Size of the unpatched command batch. |
* @sw_context: Structure holding the relocation lists. |
* |
* Side effects: If this function returns 0, then the command batch |
* pointed to by @kernel_commands will have been modified. |
*/ |
static int vmw_execbuf_submit_fifo(struct vmw_private *dev_priv, |
void *kernel_commands, |
u32 command_size, |
struct vmw_sw_context *sw_context) |
{ |
void *cmd; |
|
if (sw_context->dx_ctx_node) |
cmd = vmw_fifo_reserve_dx(dev_priv, command_size, |
sw_context->dx_ctx_node->res->id); |
else |
cmd = vmw_fifo_reserve(dev_priv, command_size); |
if (!cmd) { |
DRM_ERROR("Failed reserving fifo space for commands.\n"); |
return -ENOMEM; |
} |
|
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); |
|
return 0; |
} |
|
/** |
* vmw_execbuf_submit_cmdbuf - Patch a command batch and submit it using |
* the command buffer manager. |
* |
* @dev_priv: Pointer to a device private structure. |
* @header: Opaque handle to the command buffer allocation. |
* @command_size: Size of the unpatched command batch. |
* @sw_context: Structure holding the relocation lists. |
* |
* Side effects: If this function returns 0, then the command buffer |
* represented by @header will have been modified. |
*/ |
static int vmw_execbuf_submit_cmdbuf(struct vmw_private *dev_priv, |
struct vmw_cmdbuf_header *header, |
u32 command_size, |
struct vmw_sw_context *sw_context) |
{ |
u32 id = ((sw_context->dx_ctx_node) ? sw_context->dx_ctx_node->res->id : |
SVGA3D_INVALID_ID); |
void *cmd = vmw_cmdbuf_reserve(dev_priv->cman, command_size, |
id, false, header); |
|
vmw_apply_relocations(sw_context); |
vmw_resource_relocations_apply(cmd, &sw_context->res_relocations); |
vmw_resource_relocations_free(&sw_context->res_relocations); |
vmw_cmdbuf_commit(dev_priv->cman, command_size, header, false); |
|
return 0; |
} |
|
/** |
* vmw_execbuf_cmdbuf - Prepare, if possible, a user-space command batch for |
* submission using a command buffer. |
* |
* @dev_priv: Pointer to a device private structure. |
* @user_commands: User-space pointer to the commands to be submitted. |
* @command_size: Size of the unpatched command batch. |
* @header: Out parameter returning the opaque pointer to the command buffer. |
* |
* This function checks whether we can use the command buffer manager for |
* submission and if so, creates a command buffer of suitable size and |
* copies the user data into that buffer. |
* |
* On successful return, the function returns a pointer to the data in the |
* command buffer and *@header is set to non-NULL. |
* If command buffers could not be used, the function will return the value |
* of @kernel_commands on function call. That value may be NULL. In that case, |
* the value of *@header will be set to NULL. |
* If an error is encountered, the function will return a pointer error value. |
* If the function is interrupted by a signal while sleeping, it will return |
* -ERESTARTSYS casted to a pointer error value. |
*/ |
static void *vmw_execbuf_cmdbuf(struct vmw_private *dev_priv, |
void __user *user_commands, |
void *kernel_commands, |
u32 command_size, |
struct vmw_cmdbuf_header **header) |
{ |
size_t cmdbuf_size; |
int ret; |
|
*header = NULL; |
if (!dev_priv->cman || kernel_commands) |
return kernel_commands; |
|
if (command_size > SVGA_CB_MAX_SIZE) { |
DRM_ERROR("Command buffer is too large.\n"); |
return ERR_PTR(-EINVAL); |
} |
|
/* If possible, add a little space for fencing. */ |
cmdbuf_size = command_size + 512; |
cmdbuf_size = min_t(size_t, cmdbuf_size, SVGA_CB_MAX_SIZE); |
kernel_commands = vmw_cmdbuf_alloc(dev_priv->cman, cmdbuf_size, |
true, header); |
if (IS_ERR(kernel_commands)) |
return kernel_commands; |
|
ret = copy_from_user(kernel_commands, user_commands, |
command_size); |
if (ret) { |
DRM_ERROR("Failed copying commands.\n"); |
vmw_cmdbuf_header_free(*header); |
*header = NULL; |
return ERR_PTR(-EFAULT); |
} |
|
return kernel_commands; |
} |
|
static int vmw_execbuf_tie_context(struct vmw_private *dev_priv, |
struct vmw_sw_context *sw_context, |
uint32_t handle) |
{ |
struct vmw_resource_val_node *ctx_node; |
struct vmw_resource *res; |
int ret; |
|
if (handle == SVGA3D_INVALID_ID) |
return 0; |
|
ret = vmw_user_resource_lookup_handle(dev_priv, sw_context->fp->tfile, |
handle, user_context_converter, |
&res); |
if (unlikely(ret != 0)) { |
DRM_ERROR("Could not find or user DX context 0x%08x.\n", |
(unsigned) handle); |
return ret; |
} |
|
ret = vmw_resource_val_add(sw_context, res, &ctx_node); |
if (unlikely(ret != 0)) |
goto out_err; |
|
sw_context->dx_ctx_node = ctx_node; |
sw_context->man = vmw_context_res_man(res); |
out_err: |
vmw_resource_unreference(&res); |
return ret; |
} |
|
int vmw_execbuf_process(struct drm_file *file_priv, |
struct vmw_private *dev_priv, |
void __user *user_commands, |
2340,6 → 3754,7 |
void *kernel_commands, |
uint32_t command_size, |
uint64_t throttle_us, |
uint32_t dx_context_handle, |
struct drm_vmw_fence_rep __user *user_fence_rep, |
struct vmw_fence_obj **out_fence) |
{ |
2347,19 → 3762,33 |
struct vmw_fence_obj *fence = NULL; |
struct vmw_resource *error_resource; |
struct list_head resource_list; |
struct vmw_cmdbuf_header *header; |
struct ww_acquire_ctx ticket; |
uint32_t handle; |
void *cmd; |
int ret; |
|
if (throttle_us) { |
ret = vmw_wait_lag(dev_priv, &dev_priv->fifo.marker_queue, |
throttle_us); |
|
if (ret) |
return ret; |
} |
|
kernel_commands = vmw_execbuf_cmdbuf(dev_priv, user_commands, |
kernel_commands, command_size, |
&header); |
if (IS_ERR(kernel_commands)) |
return PTR_ERR(kernel_commands); |
|
ret = mutex_lock_interruptible(&dev_priv->cmdbuf_mutex); |
if (unlikely(ret != 0)) |
return -ERESTARTSYS; |
if (ret) { |
ret = -ERESTARTSYS; |
goto out_free_header; |
} |
|
/* |
sw_context->kernel = false; |
if (kernel_commands == NULL) { |
sw_context->kernel = false; |
|
ret = vmw_resize_cmd_bounce(sw_context, command_size); |
if (unlikely(ret != 0)) |
goto out_unlock; |
2374,20 → 3803,26 |
goto out_unlock; |
} |
kernel_commands = sw_context->cmd_bounce; |
} else */ |
} else if (!header) |
sw_context->kernel = true; |
|
sw_context->fp = vmw_fpriv(file_priv); |
sw_context->cur_reloc = 0; |
sw_context->cur_val_buf = 0; |
sw_context->fence_flags = 0; |
INIT_LIST_HEAD(&sw_context->resource_list); |
INIT_LIST_HEAD(&sw_context->ctx_resource_list); |
sw_context->cur_query_bo = dev_priv->pinned_bo; |
sw_context->last_query_ctx = NULL; |
sw_context->needs_post_query_barrier = false; |
sw_context->dx_ctx_node = NULL; |
sw_context->dx_query_mob = NULL; |
sw_context->dx_query_ctx = NULL; |
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->staged_bindings) |
vmw_binding_state_reset(sw_context->staged_bindings); |
|
if (!sw_context->res_ht_initialized) { |
ret = drm_ht_create(&sw_context->res_ht, VMW_RES_HT_ORDER); |
if (unlikely(ret != 0)) |
2395,10 → 3830,24 |
sw_context->res_ht_initialized = true; |
} |
INIT_LIST_HEAD(&sw_context->staged_cmd_res); |
INIT_LIST_HEAD(&resource_list); |
ret = vmw_execbuf_tie_context(dev_priv, sw_context, dx_context_handle); |
if (unlikely(ret != 0)) { |
list_splice_init(&sw_context->ctx_resource_list, |
&sw_context->resource_list); |
goto out_err_nores; |
} |
|
INIT_LIST_HEAD(&resource_list); |
ret = vmw_cmd_check_all(dev_priv, sw_context, kernel_commands, |
command_size); |
/* |
* Merge the resource lists before checking the return status |
* from vmd_cmd_check_all so that all the open hashtabs will |
* be handled properly even if vmw_cmd_check_all fails. |
*/ |
list_splice_init(&sw_context->ctx_resource_list, |
&sw_context->resource_list); |
|
if (unlikely(ret != 0)) |
goto out_err_nores; |
|
2406,9 → 3855,10 |
if (unlikely(ret != 0)) |
goto out_err_nores; |
|
ret = ttm_eu_reserve_buffers(&ticket, &sw_context->validate_nodes); |
ret = ttm_eu_reserve_buffers(&ticket, &sw_context->validate_nodes, |
true, NULL); |
if (unlikely(ret != 0)) |
goto out_err; |
goto out_err_nores; |
|
ret = vmw_validate_buffers(dev_priv, sw_context); |
if (unlikely(ret != 0)) |
2418,14 → 3868,6 |
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_err; |
} |
|
ret = mutex_lock_interruptible(&dev_priv->binding_mutex); |
if (unlikely(ret != 0)) { |
ret = -ERESTARTSYS; |
2438,21 → 3880,18 |
goto out_unlock_binding; |
} |
|
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_unlock_binding; |
if (!header) { |
ret = vmw_execbuf_submit_fifo(dev_priv, kernel_commands, |
command_size, sw_context); |
} else { |
ret = vmw_execbuf_submit_cmdbuf(dev_priv, header, command_size, |
sw_context); |
header = NULL; |
} |
mutex_unlock(&dev_priv->binding_mutex); |
if (ret) |
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); |
ret = vmw_execbuf_fence_commands(file_priv, dev_priv, |
&fence, |
2466,8 → 3905,7 |
if (ret != 0) |
DRM_ERROR("Fence submission error. Syncing.\n"); |
|
vmw_resource_list_unreserve(&sw_context->resource_list, false); |
mutex_unlock(&dev_priv->binding_mutex); |
vmw_resources_unreserve(sw_context, false); |
|
ttm_eu_fence_buffer_objects(&ticket, &sw_context->validate_nodes, |
(void *) fence); |
2496,7 → 3934,7 |
* Unreference resources outside of the cmdbuf_mutex to |
* avoid deadlocks in resource destruction paths. |
*/ |
vmw_resource_list_unreference(&resource_list); |
vmw_resource_list_unreference(sw_context, &resource_list); |
|
return 0; |
|
2505,7 → 3943,7 |
out_err: |
ttm_eu_backoff_reservation(&ticket, &sw_context->validate_nodes); |
out_err_nores: |
vmw_resource_list_unreserve(&sw_context->resource_list, true); |
vmw_resources_unreserve(sw_context, true); |
vmw_resource_relocations_free(&sw_context->res_relocations); |
vmw_free_relocations(sw_context); |
vmw_clear_validations(sw_context); |
2523,9 → 3961,12 |
* Unreference resources outside of the cmdbuf_mutex to |
* avoid deadlocks in resource destruction paths. |
*/ |
vmw_resource_list_unreference(&resource_list); |
vmw_resource_list_unreference(sw_context, &resource_list); |
if (unlikely(error_resource != NULL)) |
vmw_resource_unreference(&error_resource); |
out_free_header: |
if (header) |
vmw_cmdbuf_header_free(header); |
|
return ret; |
} |
2544,10 → 3985,12 |
DRM_ERROR("Can't unpin query buffer. Trying to recover.\n"); |
|
(void) vmw_fallback_wait(dev_priv, false, true, 0, false, 10*HZ); |
vmw_bo_pin(dev_priv->pinned_bo, false); |
vmw_bo_pin(dev_priv->dummy_query_bo, false); |
vmw_bo_pin_reserved(dev_priv->pinned_bo, false); |
if (dev_priv->dummy_query_bo_pinned) { |
vmw_bo_pin_reserved(dev_priv->dummy_query_bo, false); |
dev_priv->dummy_query_bo_pinned = false; |
} |
} |
|
|
/** |
2588,16 → 4031,16 |
|
INIT_LIST_HEAD(&validate_list); |
|
pinned_val.bo = ttm_bo_reference(dev_priv->pinned_bo); |
pinned_val.bo = ttm_bo_reference(&dev_priv->pinned_bo->base); |
pinned_val.shared = false; |
list_add_tail(&pinned_val.head, &validate_list); |
|
query_val.bo = ttm_bo_reference(dev_priv->dummy_query_bo); |
query_val.bo = ttm_bo_reference(&dev_priv->dummy_query_bo->base); |
query_val.shared = false; |
list_add_tail(&query_val.head, &validate_list); |
|
do { |
ret = ttm_eu_reserve_buffers(&ticket, &validate_list); |
} while (ret == -ERESTARTSYS); |
|
ret = ttm_eu_reserve_buffers(&ticket, &validate_list, |
false, NULL); |
if (unlikely(ret != 0)) { |
vmw_execbuf_unpin_panic(dev_priv); |
goto out_no_reserve; |
2613,10 → 4056,11 |
dev_priv->query_cid_valid = false; |
} |
|
vmw_bo_pin(dev_priv->pinned_bo, false); |
vmw_bo_pin(dev_priv->dummy_query_bo, false); |
vmw_bo_pin_reserved(dev_priv->pinned_bo, false); |
if (dev_priv->dummy_query_bo_pinned) { |
vmw_bo_pin_reserved(dev_priv->dummy_query_bo, false); |
dev_priv->dummy_query_bo_pinned = false; |
|
} |
if (fence == NULL) { |
(void) vmw_execbuf_fence_commands(NULL, dev_priv, &lfence, |
NULL); |
2628,7 → 4072,9 |
|
ttm_bo_unref(&query_val.bo); |
ttm_bo_unref(&pinned_val.bo); |
ttm_bo_unref(&dev_priv->pinned_bo); |
vmw_dmabuf_unreference(&dev_priv->pinned_bo); |
DRM_INFO("Dummy query bo pin count: %d\n", |
dev_priv->dummy_query_bo->pin_count); |
|
out_unlock: |
return; |
2638,7 → 4084,7 |
out_no_reserve: |
ttm_bo_unref(&query_val.bo); |
ttm_bo_unref(&pinned_val.bo); |
ttm_bo_unref(&dev_priv->pinned_bo); |
vmw_dmabuf_unreference(&dev_priv->pinned_bo); |
} |
|
/** |
2667,44 → 4113,74 |
mutex_unlock(&dev_priv->cmdbuf_mutex); |
} |
|
|
int vmw_execbuf_ioctl(struct drm_device *dev, void *data, |
struct drm_file *file_priv) |
int vmw_execbuf_ioctl(struct drm_device *dev, unsigned long data, |
struct drm_file *file_priv, size_t size) |
{ |
struct vmw_private *dev_priv = vmw_priv(dev); |
struct drm_vmw_execbuf_arg *arg = (struct drm_vmw_execbuf_arg *)data; |
struct drm_vmw_execbuf_arg arg; |
int ret; |
static const size_t copy_offset[] = { |
offsetof(struct drm_vmw_execbuf_arg, context_handle), |
sizeof(struct drm_vmw_execbuf_arg)}; |
|
if (unlikely(size < copy_offset[0])) { |
DRM_ERROR("Invalid command size, ioctl %d\n", |
DRM_VMW_EXECBUF); |
return -EINVAL; |
} |
|
if (copy_from_user(&arg, (void __user *) data, copy_offset[0]) != 0) |
return -EFAULT; |
|
/* |
* This will allow us to extend the ioctl argument while |
* Extend the ioctl argument while |
* maintaining backwards compatibility: |
* We take different code paths depending on the value of |
* arg->version. |
* arg.version. |
*/ |
|
if (unlikely(arg->version != DRM_VMW_EXECBUF_VERSION)) { |
if (unlikely(arg.version > DRM_VMW_EXECBUF_VERSION || |
arg.version == 0)) { |
DRM_ERROR("Incorrect execbuf version.\n"); |
DRM_ERROR("You're running outdated experimental " |
"vmwgfx user-space drivers."); |
return -EINVAL; |
} |
|
if (arg.version > 1 && |
copy_from_user(&arg.context_handle, |
(void __user *) (data + copy_offset[0]), |
copy_offset[arg.version - 1] - |
copy_offset[0]) != 0) |
return -EFAULT; |
|
switch (arg.version) { |
case 1: |
arg.context_handle = (uint32_t) -1; |
break; |
case 2: |
if (arg.pad64 != 0) { |
DRM_ERROR("Unused IOCTL data not set to zero.\n"); |
return -EINVAL; |
} |
break; |
default: |
break; |
} |
|
ret = ttm_read_lock(&dev_priv->reservation_sem, true); |
if (unlikely(ret != 0)) |
return ret; |
|
ret = vmw_execbuf_process(file_priv, dev_priv, |
(void __user *)(unsigned long)arg->commands, |
NULL, arg->command_size, arg->throttle_us, |
(void __user *)(unsigned long)arg->fence_rep, |
(void __user *)(unsigned long)arg.commands, |
NULL, arg.command_size, arg.throttle_us, |
arg.context_handle, |
(void __user *)(unsigned long)arg.fence_rep, |
NULL); |
|
ttm_read_unlock(&dev_priv->reservation_sem); |
if (unlikely(ret != 0)) |
goto out_unlock; |
return ret; |
|
// vmw_kms_cursor_post_execbuf(dev_priv); |
|
out_unlock: |
ttm_read_unlock(&dev_priv->reservation_sem); |
return ret; |
return 0; |
} |