These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / drivers / gpu / drm / vmwgfx / vmwgfx_so.c
diff --git a/kernel/drivers/gpu/drm/vmwgfx/vmwgfx_so.c b/kernel/drivers/gpu/drm/vmwgfx/vmwgfx_so.c
new file mode 100644 (file)
index 0000000..5a73eeb
--- /dev/null
@@ -0,0 +1,555 @@
+/**************************************************************************
+ * Copyright © 2014-2015 VMware, Inc., Palo Alto, CA., USA
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ **************************************************************************/
+
+#include "vmwgfx_drv.h"
+#include "vmwgfx_resource_priv.h"
+#include "vmwgfx_so.h"
+#include "vmwgfx_binding.h"
+
+/*
+ * The currently only reason we need to keep track of views is that if we
+ * destroy a hardware surface, all views pointing to it must also be destroyed,
+ * otherwise the device will error.
+ * So in particuar if a surface is evicted, we must destroy all views pointing
+ * to it, and all context bindings of that view. Similarly we must restore
+ * the view bindings, views and surfaces pointed to by the views when a
+ * context is referenced in the command stream.
+ */
+
+/**
+ * struct vmw_view - view metadata
+ *
+ * @res: The struct vmw_resource we derive from
+ * @ctx: Non-refcounted pointer to the context this view belongs to.
+ * @srf: Refcounted pointer to the surface pointed to by this view.
+ * @cotable: Refcounted pointer to the cotable holding this view.
+ * @srf_head: List head for the surface-to-view list.
+ * @cotable_head: List head for the cotable-to_view list.
+ * @view_type: View type.
+ * @view_id: User-space per context view id. Currently used also as per
+ * context device view id.
+ * @cmd_size: Size of the SVGA3D define view command that we've copied from the
+ * command stream.
+ * @committed: Whether the view is actually created or pending creation at the
+ * device level.
+ * @cmd: The SVGA3D define view command copied from the command stream.
+ */
+struct vmw_view {
+       struct rcu_head rcu;
+       struct vmw_resource res;
+       struct vmw_resource *ctx;      /* Immutable */
+       struct vmw_resource *srf;      /* Immutable */
+       struct vmw_resource *cotable;  /* Immutable */
+       struct list_head srf_head;     /* Protected by binding_mutex */
+       struct list_head cotable_head; /* Protected by binding_mutex */
+       unsigned view_type;            /* Immutable */
+       unsigned view_id;              /* Immutable */
+       u32 cmd_size;                  /* Immutable */
+       bool committed;                /* Protected by binding_mutex */
+       u32 cmd[1];                    /* Immutable */
+};
+
+static int vmw_view_create(struct vmw_resource *res);
+static int vmw_view_destroy(struct vmw_resource *res);
+static void vmw_hw_view_destroy(struct vmw_resource *res);
+static void vmw_view_commit_notify(struct vmw_resource *res,
+                                  enum vmw_cmdbuf_res_state state);
+
+static const struct vmw_res_func vmw_view_func = {
+       .res_type = vmw_res_view,
+       .needs_backup = false,
+       .may_evict = false,
+       .type_name = "DX view",
+       .backup_placement = NULL,
+       .create = vmw_view_create,
+       .commit_notify = vmw_view_commit_notify,
+};
+
+/**
+ * struct vmw_view - view define command body stub
+ *
+ * @view_id: The device id of the view being defined
+ * @sid: The surface id of the view being defined
+ *
+ * This generic struct is used by the code to change @view_id and @sid of a
+ * saved view define command.
+ */
+struct vmw_view_define {
+       uint32 view_id;
+       uint32 sid;
+};
+
+/**
+ * vmw_view - Convert a struct vmw_resource to a struct vmw_view
+ *
+ * @res: Pointer to the resource to convert.
+ *
+ * Returns a pointer to a struct vmw_view.
+ */
+static struct vmw_view *vmw_view(struct vmw_resource *res)
+{
+       return container_of(res, struct vmw_view, res);
+}
+
+/**
+ * vmw_view_commit_notify - Notify that a view operation has been committed to
+ * hardware from a user-supplied command stream.
+ *
+ * @res: Pointer to the view resource.
+ * @state: Indicating whether a creation or removal has been committed.
+ *
+ */
+static void vmw_view_commit_notify(struct vmw_resource *res,
+                                  enum vmw_cmdbuf_res_state state)
+{
+       struct vmw_view *view = vmw_view(res);
+       struct vmw_private *dev_priv = res->dev_priv;
+
+       mutex_lock(&dev_priv->binding_mutex);
+       if (state == VMW_CMDBUF_RES_ADD) {
+               struct vmw_surface *srf = vmw_res_to_srf(view->srf);
+
+               list_add_tail(&view->srf_head, &srf->view_list);
+               vmw_cotable_add_resource(view->cotable, &view->cotable_head);
+               view->committed = true;
+               res->id = view->view_id;
+
+       } else {
+               list_del_init(&view->cotable_head);
+               list_del_init(&view->srf_head);
+               view->committed = false;
+               res->id = -1;
+       }
+       mutex_unlock(&dev_priv->binding_mutex);
+}
+
+/**
+ * vmw_view_create - Create a hardware view.
+ *
+ * @res: Pointer to the view resource.
+ *
+ * Create a hardware view. Typically used if that view has previously been
+ * destroyed by an eviction operation.
+ */
+static int vmw_view_create(struct vmw_resource *res)
+{
+       struct vmw_view *view = vmw_view(res);
+       struct vmw_surface *srf = vmw_res_to_srf(view->srf);
+       struct vmw_private *dev_priv = res->dev_priv;
+       struct {
+               SVGA3dCmdHeader header;
+               struct vmw_view_define body;
+       } *cmd;
+
+       mutex_lock(&dev_priv->binding_mutex);
+       if (!view->committed) {
+               mutex_unlock(&dev_priv->binding_mutex);
+               return 0;
+       }
+
+       cmd = vmw_fifo_reserve_dx(res->dev_priv, view->cmd_size,
+                                 view->ctx->id);
+       if (!cmd) {
+               DRM_ERROR("Failed reserving FIFO space for view creation.\n");
+               mutex_unlock(&dev_priv->binding_mutex);
+               return -ENOMEM;
+       }
+       memcpy(cmd, &view->cmd, view->cmd_size);
+       WARN_ON(cmd->body.view_id != view->view_id);
+       /* Sid may have changed due to surface eviction. */
+       WARN_ON(view->srf->id == SVGA3D_INVALID_ID);
+       cmd->body.sid = view->srf->id;
+       vmw_fifo_commit(res->dev_priv, view->cmd_size);
+       res->id = view->view_id;
+       list_add_tail(&view->srf_head, &srf->view_list);
+       vmw_cotable_add_resource(view->cotable, &view->cotable_head);
+       mutex_unlock(&dev_priv->binding_mutex);
+
+       return 0;
+}
+
+/**
+ * vmw_view_destroy - Destroy a hardware view.
+ *
+ * @res: Pointer to the view resource.
+ *
+ * Destroy a hardware view. Typically used on unexpected termination of the
+ * owning process or if the surface the view is pointing to is destroyed.
+ */
+static int vmw_view_destroy(struct vmw_resource *res)
+{
+       struct vmw_private *dev_priv = res->dev_priv;
+       struct vmw_view *view = vmw_view(res);
+       struct {
+               SVGA3dCmdHeader header;
+               union vmw_view_destroy body;
+       } *cmd;
+
+       WARN_ON_ONCE(!mutex_is_locked(&dev_priv->binding_mutex));
+       vmw_binding_res_list_scrub(&res->binding_head);
+
+       if (!view->committed || res->id == -1)
+               return 0;
+
+       cmd = vmw_fifo_reserve_dx(dev_priv, sizeof(*cmd), view->ctx->id);
+       if (!cmd) {
+               DRM_ERROR("Failed reserving FIFO space for view "
+                         "destruction.\n");
+               return -ENOMEM;
+       }
+
+       cmd->header.id = vmw_view_destroy_cmds[view->view_type];
+       cmd->header.size = sizeof(cmd->body);
+       cmd->body.view_id = view->view_id;
+       vmw_fifo_commit(dev_priv, sizeof(*cmd));
+       res->id = -1;
+       list_del_init(&view->cotable_head);
+       list_del_init(&view->srf_head);
+
+       return 0;
+}
+
+/**
+ * vmw_hw_view_destroy - Destroy a hardware view as part of resource cleanup.
+ *
+ * @res: Pointer to the view resource.
+ *
+ * Destroy a hardware view if it's still present.
+ */
+static void vmw_hw_view_destroy(struct vmw_resource *res)
+{
+       struct vmw_private *dev_priv = res->dev_priv;
+
+       mutex_lock(&dev_priv->binding_mutex);
+       WARN_ON(vmw_view_destroy(res));
+       res->id = -1;
+       mutex_unlock(&dev_priv->binding_mutex);
+}
+
+/**
+ * vmw_view_key - Compute a view key suitable for the cmdbuf resource manager
+ *
+ * @user_key: The user-space id used for the view.
+ * @view_type: The view type.
+ *
+ * Destroy a hardware view if it's still present.
+ */
+static u32 vmw_view_key(u32 user_key, enum vmw_view_type view_type)
+{
+       return user_key | (view_type << 20);
+}
+
+/**
+ * vmw_view_id_ok - Basic view id and type range checks.
+ *
+ * @user_key: The user-space id used for the view.
+ * @view_type: The view type.
+ *
+ * Checks that the view id and type (typically provided by user-space) is
+ * valid.
+ */
+static bool vmw_view_id_ok(u32 user_key, enum vmw_view_type view_type)
+{
+       return (user_key < SVGA_COTABLE_MAX_IDS &&
+               view_type < vmw_view_max);
+}
+
+/**
+ * vmw_view_res_free - resource res_free callback for view resources
+ *
+ * @res: Pointer to a struct vmw_resource
+ *
+ * Frees memory and memory accounting held by a struct vmw_view.
+ */
+static void vmw_view_res_free(struct vmw_resource *res)
+{
+       struct vmw_view *view = vmw_view(res);
+       size_t size = offsetof(struct vmw_view, cmd) + view->cmd_size;
+       struct vmw_private *dev_priv = res->dev_priv;
+
+       vmw_resource_unreference(&view->cotable);
+       vmw_resource_unreference(&view->srf);
+       kfree_rcu(view, rcu);
+       ttm_mem_global_free(vmw_mem_glob(dev_priv), size);
+}
+
+/**
+ * vmw_view_add - Create a view resource and stage it for addition
+ * as a command buffer managed resource.
+ *
+ * @man: Pointer to the compat shader manager identifying the shader namespace.
+ * @ctx: Pointer to a struct vmw_resource identifying the active context.
+ * @srf: Pointer to a struct vmw_resource identifying the surface the view
+ * points to.
+ * @view_type: The view type deduced from the view create command.
+ * @user_key: The key that is used to identify the shader. The key is
+ * unique to the view type and to the context.
+ * @cmd: Pointer to the view create command in the command stream.
+ * @cmd_size: Size of the view create command in the command stream.
+ * @list: Caller's list of staged command buffer resource actions.
+ */
+int vmw_view_add(struct vmw_cmdbuf_res_manager *man,
+                struct vmw_resource *ctx,
+                struct vmw_resource *srf,
+                enum vmw_view_type view_type,
+                u32 user_key,
+                const void *cmd,
+                size_t cmd_size,
+                struct list_head *list)
+{
+       static const size_t vmw_view_define_sizes[] = {
+               [vmw_view_sr] = sizeof(SVGA3dCmdDXDefineShaderResourceView),
+               [vmw_view_rt] = sizeof(SVGA3dCmdDXDefineRenderTargetView),
+               [vmw_view_ds] = sizeof(SVGA3dCmdDXDefineDepthStencilView)
+       };
+
+       struct vmw_private *dev_priv = ctx->dev_priv;
+       struct vmw_resource *res;
+       struct vmw_view *view;
+       size_t size;
+       int ret;
+
+       if (cmd_size != vmw_view_define_sizes[view_type] +
+           sizeof(SVGA3dCmdHeader)) {
+               DRM_ERROR("Illegal view create command size.\n");
+               return -EINVAL;
+       }
+
+       if (!vmw_view_id_ok(user_key, view_type)) {
+               DRM_ERROR("Illegal view add view id.\n");
+               return -EINVAL;
+       }
+
+       size = offsetof(struct vmw_view, cmd) + cmd_size;
+
+       ret = ttm_mem_global_alloc(vmw_mem_glob(dev_priv), size, false, true);
+       if (ret) {
+               if (ret != -ERESTARTSYS)
+                       DRM_ERROR("Out of graphics memory for view"
+                                 " creation.\n");
+               return ret;
+       }
+
+       view = kmalloc(size, GFP_KERNEL);
+       if (!view) {
+               ttm_mem_global_free(vmw_mem_glob(dev_priv), size);
+               return -ENOMEM;
+       }
+
+       res = &view->res;
+       view->ctx = ctx;
+       view->srf = vmw_resource_reference(srf);
+       view->cotable = vmw_context_cotable(ctx, vmw_view_cotables[view_type]);
+       view->view_type = view_type;
+       view->view_id = user_key;
+       view->cmd_size = cmd_size;
+       view->committed = false;
+       INIT_LIST_HEAD(&view->srf_head);
+       INIT_LIST_HEAD(&view->cotable_head);
+       memcpy(&view->cmd, cmd, cmd_size);
+       ret = vmw_resource_init(dev_priv, res, true,
+                               vmw_view_res_free, &vmw_view_func);
+       if (ret)
+               goto out_resource_init;
+
+       ret = vmw_cmdbuf_res_add(man, vmw_cmdbuf_res_view,
+                                vmw_view_key(user_key, view_type),
+                                res, list);
+       if (ret)
+               goto out_resource_init;
+
+       res->id = view->view_id;
+       vmw_resource_activate(res, vmw_hw_view_destroy);
+
+out_resource_init:
+       vmw_resource_unreference(&res);
+
+       return ret;
+}
+
+/**
+ * vmw_view_remove - Stage a view for removal.
+ *
+ * @man: Pointer to the view manager identifying the shader namespace.
+ * @user_key: The key that is used to identify the view. The key is
+ * unique to the view type.
+ * @view_type: View type
+ * @list: Caller's list of staged command buffer resource actions.
+ * @res_p: If the resource is in an already committed state, points to the
+ * struct vmw_resource on successful return. The pointer will be
+ * non ref-counted.
+ */
+int vmw_view_remove(struct vmw_cmdbuf_res_manager *man,
+                   u32 user_key, enum vmw_view_type view_type,
+                   struct list_head *list,
+                   struct vmw_resource **res_p)
+{
+       if (!vmw_view_id_ok(user_key, view_type)) {
+               DRM_ERROR("Illegal view remove view id.\n");
+               return -EINVAL;
+       }
+
+       return vmw_cmdbuf_res_remove(man, vmw_cmdbuf_res_view,
+                                    vmw_view_key(user_key, view_type),
+                                    list, res_p);
+}
+
+/**
+ * vmw_view_cotable_list_destroy - Evict all views belonging to a cotable.
+ *
+ * @dev_priv: Pointer to a device private struct.
+ * @list: List of views belonging to a cotable.
+ * @readback: Unused. Needed for function interface only.
+ *
+ * This function evicts all views belonging to a cotable.
+ * It must be called with the binding_mutex held, and the caller must hold
+ * a reference to the view resource. This is typically called before the
+ * cotable is paged out.
+ */
+void vmw_view_cotable_list_destroy(struct vmw_private *dev_priv,
+                                  struct list_head *list,
+                                  bool readback)
+{
+       struct vmw_view *entry, *next;
+
+       WARN_ON_ONCE(!mutex_is_locked(&dev_priv->binding_mutex));
+
+       list_for_each_entry_safe(entry, next, list, cotable_head)
+               WARN_ON(vmw_view_destroy(&entry->res));
+}
+
+/**
+ * vmw_view_surface_list_destroy - Evict all views pointing to a surface
+ *
+ * @dev_priv: Pointer to a device private struct.
+ * @list: List of views pointing to a surface.
+ *
+ * This function evicts all views pointing to a surface. This is typically
+ * called before the surface is evicted.
+ */
+void vmw_view_surface_list_destroy(struct vmw_private *dev_priv,
+                                  struct list_head *list)
+{
+       struct vmw_view *entry, *next;
+
+       WARN_ON_ONCE(!mutex_is_locked(&dev_priv->binding_mutex));
+
+       list_for_each_entry_safe(entry, next, list, srf_head)
+               WARN_ON(vmw_view_destroy(&entry->res));
+}
+
+/**
+ * vmw_view_srf - Return a non-refcounted pointer to the surface a view is
+ * pointing to.
+ *
+ * @res: pointer to a view resource.
+ *
+ * Note that the view itself is holding a reference, so as long
+ * the view resource is alive, the surface resource will be.
+ */
+struct vmw_resource *vmw_view_srf(struct vmw_resource *res)
+{
+       return vmw_view(res)->srf;
+}
+
+/**
+ * vmw_view_lookup - Look up a view.
+ *
+ * @man: The context's cmdbuf ref manager.
+ * @view_type: The view type.
+ * @user_key: The view user id.
+ *
+ * returns a refcounted pointer to a view or an error pointer if not found.
+ */
+struct vmw_resource *vmw_view_lookup(struct vmw_cmdbuf_res_manager *man,
+                                    enum vmw_view_type view_type,
+                                    u32 user_key)
+{
+       return vmw_cmdbuf_res_lookup(man, vmw_cmdbuf_res_view,
+                                    vmw_view_key(user_key, view_type));
+}
+
+const u32 vmw_view_destroy_cmds[] = {
+       [vmw_view_sr] = SVGA_3D_CMD_DX_DESTROY_SHADERRESOURCE_VIEW,
+       [vmw_view_rt] = SVGA_3D_CMD_DX_DESTROY_RENDERTARGET_VIEW,
+       [vmw_view_ds] = SVGA_3D_CMD_DX_DESTROY_DEPTHSTENCIL_VIEW,
+};
+
+const SVGACOTableType vmw_view_cotables[] = {
+       [vmw_view_sr] = SVGA_COTABLE_SRVIEW,
+       [vmw_view_rt] = SVGA_COTABLE_RTVIEW,
+       [vmw_view_ds] = SVGA_COTABLE_DSVIEW,
+};
+
+const SVGACOTableType vmw_so_cotables[] = {
+       [vmw_so_el] = SVGA_COTABLE_ELEMENTLAYOUT,
+       [vmw_so_bs] = SVGA_COTABLE_BLENDSTATE,
+       [vmw_so_ds] = SVGA_COTABLE_DEPTHSTENCIL,
+       [vmw_so_rs] = SVGA_COTABLE_RASTERIZERSTATE,
+       [vmw_so_ss] = SVGA_COTABLE_SAMPLER,
+       [vmw_so_so] = SVGA_COTABLE_STREAMOUTPUT
+};
+
+
+/* To remove unused function warning */
+static void vmw_so_build_asserts(void) __attribute__((used));
+
+
+/*
+ * This function is unused at run-time, and only used to dump various build
+ * asserts important for code optimization assumptions.
+ */
+static void vmw_so_build_asserts(void)
+{
+       /* Assert that our vmw_view_cmd_to_type() function is correct. */
+       BUILD_BUG_ON(SVGA_3D_CMD_DX_DESTROY_SHADERRESOURCE_VIEW !=
+                    SVGA_3D_CMD_DX_DEFINE_SHADERRESOURCE_VIEW + 1);
+       BUILD_BUG_ON(SVGA_3D_CMD_DX_DEFINE_RENDERTARGET_VIEW !=
+                    SVGA_3D_CMD_DX_DEFINE_SHADERRESOURCE_VIEW + 2);
+       BUILD_BUG_ON(SVGA_3D_CMD_DX_DESTROY_RENDERTARGET_VIEW !=
+                    SVGA_3D_CMD_DX_DEFINE_SHADERRESOURCE_VIEW + 3);
+       BUILD_BUG_ON(SVGA_3D_CMD_DX_DEFINE_DEPTHSTENCIL_VIEW !=
+                    SVGA_3D_CMD_DX_DEFINE_SHADERRESOURCE_VIEW + 4);
+       BUILD_BUG_ON(SVGA_3D_CMD_DX_DESTROY_DEPTHSTENCIL_VIEW !=
+                    SVGA_3D_CMD_DX_DEFINE_SHADERRESOURCE_VIEW + 5);
+
+       /* Assert that our "one body fits all" assumption is valid */
+       BUILD_BUG_ON(sizeof(union vmw_view_destroy) != sizeof(u32));
+
+       /* Assert that the view key space can hold all view ids. */
+       BUILD_BUG_ON(SVGA_COTABLE_MAX_IDS >= ((1 << 20) - 1));
+
+       /*
+        * Assert that the offset of sid in all view define commands
+        * is what we assume it to be.
+        */
+       BUILD_BUG_ON(offsetof(struct vmw_view_define, sid) !=
+                    offsetof(SVGA3dCmdDXDefineShaderResourceView, sid));
+       BUILD_BUG_ON(offsetof(struct vmw_view_define, sid) !=
+                    offsetof(SVGA3dCmdDXDefineRenderTargetView, sid));
+       BUILD_BUG_ON(offsetof(struct vmw_view_define, sid) !=
+                    offsetof(SVGA3dCmdDXDefineDepthStencilView, sid));
+}