drm/vmwgfx: Add a validation module v2
authorThomas Hellstrom <thellstrom@vmware.com>
Wed, 26 Sep 2018 13:21:13 +0000 (15:21 +0200)
committerThomas Hellstrom <thellstrom@vmware.com>
Thu, 27 Sep 2018 13:21:35 +0000 (15:21 +0200)
Isolate the functionality needed for reservation, validation and fencing
of vmwgfx buffer objects and resources and publish an API for this.

Signed-off-by: Thomas Hellstrom <thellstrom@vmware.com>
Reviewed-by: Sinclair Yeh <syeh@vmware.com>
Reviewed-by: Deepak Rawat <drawat@vmware.com> #v1
drivers/gpu/drm/vmwgfx/Makefile
drivers/gpu/drm/vmwgfx/vmwgfx_validation.c [new file with mode: 0644]
drivers/gpu/drm/vmwgfx/vmwgfx_validation.h [new file with mode: 0644]

index 22fdc07e03ad348a63af6215c84d544662df0ed3..8841bd30e1e5cb48e7f88d6cfcf97d8b3cbe1d1b 100644 (file)
@@ -8,6 +8,7 @@ vmwgfx-y := vmwgfx_execbuf.o vmwgfx_gmr.o vmwgfx_kms.o vmwgfx_drv.o \
            vmwgfx_cmdbuf_res.o vmwgfx_cmdbuf.o vmwgfx_stdu.o \
            vmwgfx_cotable.o vmwgfx_so.o vmwgfx_binding.o vmwgfx_msg.o \
            vmwgfx_simple_resource.o vmwgfx_va.o vmwgfx_blit.o \
+           vmwgfx_validation.o \
            ttm_object.o ttm_lock.o
 
 obj-$(CONFIG_DRM_VMWGFX) := vmwgfx.o
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_validation.c b/drivers/gpu/drm/vmwgfx/vmwgfx_validation.c
new file mode 100644 (file)
index 0000000..5bc00ea
--- /dev/null
@@ -0,0 +1,659 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/**************************************************************************
+ *
+ * Copyright © 2018 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 <linux/slab.h>
+#include "vmwgfx_validation.h"
+#include "vmwgfx_drv.h"
+
+/**
+ * struct vmw_validation_bo_node - Buffer object validation metadata.
+ * @base: Metadata used for TTM reservation- and validation.
+ * @hash: A hash entry used for the duplicate detection hash table.
+ * @as_mob: Validate as mob.
+ * @cpu_blit: Validate for cpu blit access.
+ *
+ * Bit fields are used since these structures are allocated and freed in
+ * large numbers and space conservation is desired.
+ */
+struct vmw_validation_bo_node {
+       struct ttm_validate_buffer base;
+       struct drm_hash_item hash;
+       u32 as_mob : 1;
+       u32 cpu_blit : 1;
+};
+
+/**
+ * struct vmw_validation_res_node - Resource validation metadata.
+ * @head: List head for the resource validation list.
+ * @hash: A hash entry used for the duplicate detection hash table.
+ * @res: Reference counted resource pointer.
+ * @new_backup: Non ref-counted pointer to new backup buffer to be assigned
+ * to a resource.
+ * @new_backup_offset: Offset into the new backup mob for resources that can
+ * share MOBs.
+ * @no_buffer_needed: Kernel does not need to allocate a MOB during validation,
+ * the command stream provides a mob bind operation.
+ * @switching_backup: The validation process is switching backup MOB.
+ * @first_usage: True iff the resource has been seen only once in the current
+ * validation batch.
+ * @reserved: Whether the resource is currently reserved by this process.
+ * @private: Optionally additional memory for caller-private data.
+ *
+ * Bit fields are used since these structures are allocated and freed in
+ * large numbers and space conservation is desired.
+ */
+struct vmw_validation_res_node {
+       struct list_head head;
+       struct drm_hash_item hash;
+       struct vmw_resource *res;
+       struct vmw_buffer_object *new_backup;
+       unsigned long new_backup_offset;
+       u32 no_buffer_needed : 1;
+       u32 switching_backup : 1;
+       u32 first_usage : 1;
+       u32 reserved : 1;
+       unsigned long private[0];
+};
+
+/**
+ * vmw_validation_find_bo_dup - Find a duplicate buffer object entry in the
+ * validation context's lists.
+ * @ctx: The validation context to search.
+ * @vbo: The buffer object to search for.
+ *
+ * Return: Pointer to the struct vmw_validation_bo_node referencing the
+ * duplicate, or NULL if none found.
+ */
+static struct vmw_validation_bo_node *
+vmw_validation_find_bo_dup(struct vmw_validation_context *ctx,
+                          struct vmw_buffer_object *vbo)
+{
+       struct  vmw_validation_bo_node *bo_node = NULL;
+
+       if (!ctx->merge_dups)
+               return NULL;
+
+       if (ctx->ht) {
+               struct drm_hash_item *hash;
+
+               if (!drm_ht_find_item(ctx->ht, (unsigned long) vbo, &hash))
+                       bo_node = container_of(hash, typeof(*bo_node), hash);
+       } else {
+               struct  vmw_validation_bo_node *entry;
+
+               list_for_each_entry(entry, &ctx->bo_list, base.head) {
+                       if (entry->base.bo == &vbo->base) {
+                               bo_node = entry;
+                               break;
+                       }
+               }
+       }
+
+       return bo_node;
+}
+
+/**
+ * vmw_validation_find_res_dup - Find a duplicate resource entry in the
+ * validation context's lists.
+ * @ctx: The validation context to search.
+ * @vbo: The buffer object to search for.
+ *
+ * Return: Pointer to the struct vmw_validation_bo_node referencing the
+ * duplicate, or NULL if none found.
+ */
+static struct vmw_validation_res_node *
+vmw_validation_find_res_dup(struct vmw_validation_context *ctx,
+                           struct vmw_resource *res)
+{
+       struct  vmw_validation_res_node *res_node = NULL;
+
+       if (!ctx->merge_dups)
+               return NULL;
+
+       if (ctx->ht) {
+               struct drm_hash_item *hash;
+
+               if (!drm_ht_find_item(ctx->ht, (unsigned long) res, &hash))
+                       res_node = container_of(hash, typeof(*res_node), hash);
+       } else {
+               struct  vmw_validation_res_node *entry;
+
+               list_for_each_entry(entry, &ctx->resource_ctx_list, head) {
+                       if (entry->res == res) {
+                               res_node = entry;
+                               goto out;
+                       }
+               }
+
+               list_for_each_entry(entry, &ctx->resource_list, head) {
+                       if (entry->res == res) {
+                               res_node = entry;
+                               break;
+                       }
+               }
+
+       }
+out:
+       return res_node;
+}
+
+/**
+ * vmw_validation_add_bo - Add a buffer object to the validation context.
+ * @ctx: The validation context.
+ * @vbo: The buffer object.
+ * @as_mob: Validate as mob, otherwise suitable for GMR operations.
+ * @cpu_blit: Validate in a page-mappable location.
+ *
+ * Return: Zero on success, negative error code otherwise.
+ */
+int vmw_validation_add_bo(struct vmw_validation_context *ctx,
+                         struct vmw_buffer_object *vbo,
+                         bool as_mob,
+                         bool cpu_blit)
+{
+       struct vmw_validation_bo_node *bo_node;
+
+       bo_node = vmw_validation_find_bo_dup(ctx, vbo);
+       if (bo_node) {
+               if (bo_node->as_mob != as_mob ||
+                   bo_node->cpu_blit != cpu_blit) {
+                       DRM_ERROR("Inconsistent buffer usage.\n");
+                       return -EINVAL;
+               }
+       } else {
+               struct ttm_validate_buffer *val_buf;
+               int ret;
+
+               bo_node = kmalloc(sizeof(*bo_node), GFP_KERNEL);
+               if (!bo_node)
+                       return -ENOMEM;
+
+               if (ctx->ht) {
+                       bo_node->hash.key = (unsigned long) vbo;
+                       ret = drm_ht_insert_item(ctx->ht, &bo_node->hash);
+                       if (ret) {
+                               DRM_ERROR("Failed to initialize a buffer "
+                                         "validation entry.\n");
+                               kfree(bo_node);
+                               return ret;
+                       }
+               }
+               val_buf = &bo_node->base;
+               val_buf->bo = ttm_bo_reference(&vbo->base);
+               val_buf->shared = false;
+               list_add_tail(&val_buf->head, &ctx->bo_list);
+               bo_node->as_mob = as_mob;
+               bo_node->cpu_blit = cpu_blit;
+       }
+
+       return 0;
+}
+
+/**
+ * vmw_validation_add_resource - Add a resource to the validation context.
+ * @ctx: The validation context.
+ * @res: The resource.
+ * @priv_size: Size of private, additional metadata.
+ * @p_node: Output pointer of additional metadata address.
+ * @first_usage: Whether this was the first time this resource was seen.
+ *
+ * Return: Zero on success, negative error code otherwise.
+ */
+int vmw_validation_add_resource(struct vmw_validation_context *ctx,
+                               struct vmw_resource *res,
+                               size_t priv_size,
+                               void **p_node,
+                               bool *first_usage)
+{
+       struct vmw_validation_res_node *node;
+       int ret;
+
+       node = vmw_validation_find_res_dup(ctx, res);
+       if (node) {
+               node->first_usage = 0;
+               goto out_fill;
+       }
+
+       node = kzalloc(sizeof(*node) + priv_size, GFP_KERNEL);
+       if (!node) {
+               DRM_ERROR("Failed to allocate a resource validation "
+                         "entry.\n");
+               return -ENOMEM;
+       }
+
+       if (ctx->ht) {
+               node->hash.key = (unsigned long) res;
+               ret = drm_ht_insert_item(ctx->ht, &node->hash);
+               if (ret) {
+                       DRM_ERROR("Failed to initialize a resource validation "
+                                 "entry.\n");
+                       kfree(node);
+                       return ret;
+               }
+       }
+       node->res = vmw_resource_reference(res);
+       node->first_usage = 1;
+       if (!res->dev_priv->has_mob) {
+               list_add_tail(&node->head, &ctx->resource_list);
+       } else {
+               switch (vmw_res_type(res)) {
+               case vmw_res_context:
+               case vmw_res_dx_context:
+                       list_add(&node->head, &ctx->resource_ctx_list);
+                       break;
+               case vmw_res_cotable:
+                       list_add_tail(&node->head, &ctx->resource_ctx_list);
+                       break;
+               default:
+                       list_add_tail(&node->head, &ctx->resource_list);
+                       break;
+               }
+       }
+
+out_fill:
+       if (first_usage)
+               *first_usage = node->first_usage;
+       if (p_node)
+               *p_node = &node->private;
+
+       return 0;
+}
+
+/**
+ * vmw_validation_res_switch_backup - Register a backup MOB switch during
+ * validation.
+ * @ctx: The validation context.
+ * @val_private: The additional meta-data pointer returned when the
+ * resource was registered with the validation context. Used to identify
+ * the resource.
+ * @vbo: The new backup buffer object MOB. This buffer object needs to have
+ * already been registered with the validation context.
+ * @backup_offset: Offset into the new backup MOB.
+ */
+void vmw_validation_res_switch_backup(struct vmw_validation_context *ctx,
+                                     void *val_private,
+                                     struct vmw_buffer_object *vbo,
+                                     unsigned long backup_offset)
+{
+       struct vmw_validation_res_node *val;
+
+       val = container_of(val_private, typeof(*val), private);
+
+       val->switching_backup = 1;
+       if (val->first_usage)
+               val->no_buffer_needed = 1;
+
+       val->new_backup = vbo;
+       val->new_backup_offset = backup_offset;
+}
+
+/**
+ * vmw_validation_res_reserve - Reserve all resources registered with this
+ * validation context.
+ * @ctx: The validation context.
+ * @intr: Use interruptible waits when possible.
+ *
+ * Return: Zero on success, -ERESTARTSYS if interrupted. Negative error
+ * code on failure.
+ */
+int vmw_validation_res_reserve(struct vmw_validation_context *ctx,
+                              bool intr)
+{
+       struct vmw_validation_res_node *val;
+       int ret = 0;
+
+       list_splice_init(&ctx->resource_ctx_list, &ctx->resource_list);
+
+       list_for_each_entry(val, &ctx->resource_list, head) {
+               struct vmw_resource *res = val->res;
+
+               ret = vmw_resource_reserve(res, intr, val->no_buffer_needed);
+               if (ret)
+                       goto out_unreserve;
+
+               val->reserved = 1;
+               if (res->backup) {
+                       struct vmw_buffer_object *vbo = res->backup;
+
+                       ret = vmw_validation_add_bo
+                               (ctx, vbo, vmw_resource_needs_backup(res),
+                                false);
+                       if (ret)
+                               goto out_unreserve;
+               }
+       }
+
+       return 0;
+
+out_unreserve:
+       vmw_validation_res_unreserve(ctx, true);
+       return ret;
+}
+
+/**
+ * vmw_validation_res_unreserve - Unreserve all reserved resources
+ * registered with this validation context.
+ * @ctx: The validation context.
+ * @backoff: Whether this is a backoff- of a commit-type operation. This
+ * is used to determine whether to switch backup MOBs or not.
+ */
+void vmw_validation_res_unreserve(struct vmw_validation_context *ctx,
+                                bool backoff)
+{
+       struct vmw_validation_res_node *val;
+
+       list_splice_init(&ctx->resource_ctx_list, &ctx->resource_list);
+
+       list_for_each_entry(val, &ctx->resource_list, head) {
+               if (val->reserved)
+                       vmw_resource_unreserve(val->res,
+                                              !backoff &&
+                                              val->switching_backup,
+                                              val->new_backup,
+                                              val->new_backup_offset);
+       }
+}
+
+/**
+ * vmw_validation_bo_validate_single - Validate a single buffer object.
+ * @bo: The TTM buffer object base.
+ * @interruptible: Whether to perform waits interruptible if possible.
+ * @validate_as_mob: Whether to validate in MOB memory.
+ *
+ * Return: Zero on success, -ERESTARTSYS if interrupted. Negative error
+ * code on failure.
+ */
+int vmw_validation_bo_validate_single(struct ttm_buffer_object *bo,
+                                     bool interruptible,
+                                     bool validate_as_mob)
+{
+       struct vmw_buffer_object *vbo =
+               container_of(bo, struct vmw_buffer_object, base);
+       struct ttm_operation_ctx ctx = {
+               .interruptible = interruptible,
+               .no_wait_gpu = false
+       };
+       int ret;
+
+       if (vbo->pin_count > 0)
+               return 0;
+
+       if (validate_as_mob)
+               return ttm_bo_validate(bo, &vmw_mob_placement, &ctx);
+
+       /**
+        * Put BO in VRAM if there is space, otherwise as a GMR.
+        * If there is no space in VRAM and GMR ids are all used up,
+        * start evicting GMRs to make room. If the DMA buffer can't be
+        * used as a GMR, this will return -ENOMEM.
+        */
+
+       ret = ttm_bo_validate(bo, &vmw_vram_gmr_placement, &ctx);
+       if (ret == 0 || ret == -ERESTARTSYS)
+               return ret;
+
+       /**
+        * If that failed, try VRAM again, this time evicting
+        * previous contents.
+        */
+
+       ret = ttm_bo_validate(bo, &vmw_vram_placement, &ctx);
+       return ret;
+}
+
+/**
+ * vmw_validation_bo_validate - Validate all buffer objects registered with
+ * the validation context.
+ * @ctx: The validation context.
+ * @intr: Whether to perform waits interruptible if possible.
+ *
+ * Return: Zero on success, -ERESTARTSYS if interrupted,
+ * negative error code on failure.
+ */
+int vmw_validation_bo_validate(struct vmw_validation_context *ctx, bool intr)
+{
+       struct vmw_validation_bo_node *entry;
+       int ret;
+
+       list_for_each_entry(entry, &ctx->bo_list, base.head) {
+               if (entry->cpu_blit) {
+                       struct ttm_operation_ctx ctx = {
+                               .interruptible = intr,
+                               .no_wait_gpu = false
+                       };
+
+                       ret = ttm_bo_validate(entry->base.bo,
+                                             &vmw_nonfixed_placement, &ctx);
+               } else {
+                       ret = vmw_validation_bo_validate_single
+                       (entry->base.bo, intr, entry->as_mob);
+               }
+               if (ret)
+                       return ret;
+       }
+       return 0;
+}
+
+/**
+ * vmw_validation_res_validate - Validate all resources registered with the
+ * validation context.
+ * @ctx: The validation context.
+ * @intr: Whether to perform waits interruptible if possible.
+ *
+ * Before this function is called, all resource backup buffers must have
+ * been validated.
+ *
+ * Return: Zero on success, -ERESTARTSYS if interrupted,
+ * negative error code on failure.
+ */
+int vmw_validation_res_validate(struct vmw_validation_context *ctx, bool intr)
+{
+       struct vmw_validation_res_node *val;
+       int ret;
+
+       list_for_each_entry(val, &ctx->resource_list, head) {
+               struct vmw_resource *res = val->res;
+               struct vmw_buffer_object *backup = res->backup;
+
+               ret = vmw_resource_validate(res);
+               if (ret) {
+                       if (ret != -ERESTARTSYS)
+                               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_buffer_object *vbo = res->backup;
+
+                       ret = vmw_validation_add_bo
+                               (ctx, vbo, vmw_resource_needs_backup(res),
+                                false);
+                       if (ret)
+                               return ret;
+               }
+       }
+       return 0;
+}
+
+/**
+ * vmw_validation_drop_ht - Reset the hash table used for duplicate finding
+ * and unregister it from this validation context.
+ * @ctx: The validation context.
+ *
+ * The hash table used for duplicate finding is an expensive resource and
+ * may be protected by mutexes that may cause deadlocks during resource
+ * unreferencing if held. After resource- and buffer object registering,
+ * there is no longer any use for this hash table, so allow freeing it
+ * either to shorten any mutex locking time, or before resources- and
+ * buffer objects are freed during validation context cleanup.
+ */
+void vmw_validation_drop_ht(struct vmw_validation_context *ctx)
+{
+       struct vmw_validation_bo_node *entry;
+       struct vmw_validation_res_node *val;
+
+       if (!ctx->ht)
+               return;
+
+       list_for_each_entry(entry, &ctx->bo_list, base.head)
+               (void) drm_ht_remove_item(ctx->ht, &entry->hash);
+
+       list_for_each_entry(val, &ctx->resource_list, head)
+               (void) drm_ht_remove_item(ctx->ht, &val->hash);
+
+       list_for_each_entry(val, &ctx->resource_ctx_list, head)
+               (void) drm_ht_remove_item(ctx->ht, &val->hash);
+
+       ctx->ht = NULL;
+}
+
+/**
+ * vmw_validation_unref_lists - Unregister previously registered buffer
+ * object and resources.
+ * @ctx: The validation context.
+ *
+ * Note that this function may cause buffer object- and resource destructors
+ * to be invoked.
+ */
+void vmw_validation_unref_lists(struct vmw_validation_context *ctx)
+{
+       struct vmw_validation_bo_node *entry, *next;
+       struct vmw_validation_res_node *val, *val_next;
+
+       list_for_each_entry_safe(entry, next, &ctx->bo_list, base.head) {
+               list_del(&entry->base.head);
+               ttm_bo_unref(&entry->base.bo);
+               kfree(entry);
+       }
+
+       list_splice_init(&ctx->resource_ctx_list, &ctx->resource_list);
+       list_for_each_entry_safe(val, val_next, &ctx->resource_list, head) {
+               list_del(&val->head);
+               vmw_resource_unreference(&val->res);
+               kfree(val);
+       }
+
+       WARN_ON(!list_empty(&ctx->bo_list));
+       WARN_ON(!list_empty(&ctx->resource_list));
+       WARN_ON(!list_empty(&ctx->resource_ctx_list));
+}
+
+/**
+ * vmw_validation_prepare - Prepare a validation context for command
+ * submission.
+ * @ctx: The validation context.
+ * @mutex: The mutex used to protect resource reservation.
+ * @intr: Whether to perform waits interruptible if possible.
+ *
+ * Note that the single reservation mutex @mutex is an unfortunate
+ * construct. Ideally resource reservation should be moved to per-resource
+ * ww_mutexes.
+ * If this functions doesn't return Zero to indicate success, all resources
+ * are left unreserved but still referenced.
+ * Return: Zero on success, -ERESTARTSYS if interrupted, negative error code
+ * on error.
+ */
+int vmw_validation_prepare(struct vmw_validation_context *ctx,
+                          struct mutex *mutex,
+                          bool intr)
+{
+       int ret = 0;
+
+       if (mutex) {
+               if (intr)
+                       ret = mutex_lock_interruptible(mutex);
+               else
+                       mutex_lock(mutex);
+               if (ret)
+                       return -ERESTARTSYS;
+       }
+
+       ctx->res_mutex = mutex;
+       ret = vmw_validation_res_reserve(ctx, intr);
+       if (ret)
+               goto out_no_res_reserve;
+
+       ret = vmw_validation_bo_reserve(ctx, intr);
+       if (ret)
+               goto out_no_bo_reserve;
+
+       ret = vmw_validation_bo_validate(ctx, intr);
+       if (ret)
+               goto out_no_validate;
+
+       ret = vmw_validation_res_validate(ctx, intr);
+       if (ret)
+               goto out_no_validate;
+
+       return 0;
+
+out_no_validate:
+       vmw_validation_bo_backoff(ctx);
+out_no_bo_reserve:
+       vmw_validation_res_unreserve(ctx, true);
+out_no_res_reserve:
+       if (mutex)
+               mutex_unlock(mutex);
+
+       return ret;
+}
+
+/**
+ * vmw_validation_revert - Revert validation actions if command submission
+ * failed.
+ *
+ * @ctx: The validation context.
+ *
+ * The caller still needs to unref resources after a call to this function.
+ */
+void vmw_validation_revert(struct vmw_validation_context *ctx)
+{
+       vmw_validation_bo_backoff(ctx);
+       vmw_validation_res_unreserve(ctx, true);
+       if (ctx->res_mutex)
+               mutex_unlock(ctx->res_mutex);
+}
+
+/**
+ * vmw_validation_cone - Commit validation actions after command submission
+ * success.
+ * @ctx: The validation context.
+ * @fence: Fence with which to fence all buffer objects taking part in the
+ * command submission.
+ *
+ * The caller does NOT need to unref resources after a call to this function.
+ */
+void vmw_validation_done(struct vmw_validation_context *ctx,
+                        struct vmw_fence_obj *fence)
+{
+       vmw_validation_bo_fence(ctx, fence);
+       vmw_validation_res_unreserve(ctx, false);
+       if (ctx->res_mutex)
+               mutex_unlock(ctx->res_mutex);
+       vmw_validation_unref_lists(ctx);
+}
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_validation.h b/drivers/gpu/drm/vmwgfx/vmwgfx_validation.h
new file mode 100644 (file)
index 0000000..85f9387
--- /dev/null
@@ -0,0 +1,202 @@
+/* SPDX-License-Identifier: GPL-2.0 OR MIT */
+/**************************************************************************
+ *
+ * Copyright © 2018 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.
+ *
+ **************************************************************************/
+#ifndef _VMWGFX_VALIDATION_H_
+#define _VMWGFX_VALIDATION_H_
+
+#include <drm/drm_hashtab.h>
+#include <linux/list.h>
+#include <linux/ww_mutex.h>
+#include <drm/ttm/ttm_execbuf_util.h>
+
+/**
+ * struct vmw_validation_context - Per command submission validation context
+ * @ht: Hash table used to find resource- or buffer object duplicates
+ * @resource_list: List head for resource validation metadata
+ * @resource_ctx_list: List head for resource validation metadata for
+ * resources that need to be validated before those in @resource_list
+ * @bo_list: List head for buffer objects
+ * @ticket: Ticked used for ww mutex locking
+ * @res_mutex: Pointer to mutex used for resource reserving
+ * @merge_dups: Whether to merge metadata for duplicate resources or
+ * buffer objects
+ */
+struct vmw_validation_context {
+       struct drm_open_hash *ht;
+       struct list_head resource_list;
+       struct list_head resource_ctx_list;
+       struct list_head bo_list;
+       struct ww_acquire_ctx ticket;
+       struct mutex *res_mutex;
+       unsigned int merge_dups;
+};
+
+struct vmw_buffer_object;
+struct vmw_resource;
+struct vmw_fence_obj;
+
+#if 0
+/**
+ * DECLARE_VAL_CONTEXT - Declare a validation context with initialization
+ * @_name: The name of the variable
+ * @_ht: The hash table used to find dups or NULL if none
+ * @_merge_dups: Whether to merge duplicate buffer object- or resource
+ * entries. If set to true, ideally a hash table pointer should be supplied
+ * as well unless the number of resources and buffer objects per validation
+ * is known to be very small
+ */
+#endif
+#define DECLARE_VAL_CONTEXT(_name, _ht, _merge_dups)                   \
+       struct vmw_validation_context _name =                           \
+       { .ht = _ht,                                                    \
+         .resource_list = LIST_HEAD_INIT((_name).resource_list),       \
+         .resource_ctx_list = LIST_HEAD_INIT((_name).resource_ctx_list), \
+         .bo_list = LIST_HEAD_INIT((_name).bo_list),                   \
+         .merge_dups = _merge_dups,                                    \
+         .res_mutex = NULL                                             \
+       }
+
+/**
+ * vmw_validation_has_bos - return whether the validation context has
+ * any buffer objects registered.
+ *
+ * @ctx: The validation context
+ * Returns: Whether any buffer objects are registered
+ */
+static inline bool
+vmw_validation_has_bos(struct vmw_validation_context *ctx)
+{
+       return !list_empty(&ctx->bo_list);
+}
+
+/**
+ * vmw_validation_set_ht - Register a hash table for duplicate finding
+ * @ctx: The validation context
+ * @ht: Pointer to a hash table to use for duplicate finding
+ * This function is intended to be used if the hash table wasn't
+ * available at validation context declaration time
+ */
+static inline void vmw_validation_set_ht(struct vmw_validation_context *ctx,
+                                        struct drm_open_hash *ht)
+{
+       ctx->ht = ht;
+}
+
+/**
+ * vmw_validation_bo_reserve - Reserve buffer objects registered with a
+ * validation context
+ * @ctx: The validation context
+ * @intr: Perform waits interruptible
+ *
+ * Return: Zero on success, -ERESTARTSYS when interrupted, negative error
+ * code on failure
+ */
+static inline int
+vmw_validation_bo_reserve(struct vmw_validation_context *ctx,
+                         bool intr)
+{
+       return ttm_eu_reserve_buffers(&ctx->ticket, &ctx->bo_list, intr,
+                                     NULL);
+}
+
+/**
+ * vmw_validation_bo_backoff - Unreserve buffer objects registered with a
+ * validation context
+ * @ctx: The validation context
+ *
+ * This function unreserves the buffer objects previously reserved using
+ * vmw_validation_bo_reserve. It's typically used as part of an error path
+ */
+static inline void
+vmw_validation_bo_backoff(struct vmw_validation_context *ctx)
+{
+       ttm_eu_backoff_reservation(&ctx->ticket, &ctx->bo_list);
+}
+
+/**
+ * vmw_validation_bo_fence - Unreserve and fence buffer objects registered
+ * with a validation context
+ * @ctx: The validation context
+ *
+ * This function unreserves the buffer objects previously reserved using
+ * vmw_validation_bo_reserve, and fences them with a fence object.
+ */
+static inline void
+vmw_validation_bo_fence(struct vmw_validation_context *ctx,
+                       struct vmw_fence_obj *fence)
+{
+       ttm_eu_fence_buffer_objects(&ctx->ticket, &ctx->bo_list,
+                                   (void *) fence);
+}
+
+/**
+ * vmw_validation_context_init - Initialize a validation context
+ * @ctx: Pointer to the validation context to initialize
+ *
+ * This function initializes a validation context with @merge_dups set
+ * to false
+ */
+static inline void
+vmw_validation_context_init(struct vmw_validation_context *ctx)
+{
+       memset(ctx, 0, sizeof(*ctx));
+       INIT_LIST_HEAD(&ctx->resource_list);
+       INIT_LIST_HEAD(&ctx->resource_ctx_list);
+       INIT_LIST_HEAD(&ctx->bo_list);
+}
+
+int vmw_validation_add_bo(struct vmw_validation_context *ctx,
+                         struct vmw_buffer_object *vbo,
+                         bool as_mob, bool cpu_blit);
+int vmw_validation_bo_validate_single(struct ttm_buffer_object *bo,
+                                     bool interruptible,
+                                     bool validate_as_mob);
+int vmw_validation_bo_validate(struct vmw_validation_context *ctx, bool intr);
+void vmw_validation_unref_lists(struct vmw_validation_context *ctx);
+int vmw_validation_add_resource(struct vmw_validation_context *ctx,
+                               struct vmw_resource *res,
+                               size_t priv_size,
+                               void **p_node,
+                               bool *first_usage);
+void vmw_validation_drop_ht(struct vmw_validation_context *ctx);
+int vmw_validation_res_reserve(struct vmw_validation_context *ctx,
+                              bool intr);
+void vmw_validation_res_unreserve(struct vmw_validation_context *ctx,
+                                 bool backoff);
+void vmw_validation_res_switch_backup(struct vmw_validation_context *ctx,
+                                     void *val_private,
+                                     struct vmw_buffer_object *vbo,
+                                     unsigned long backup_offset);
+int vmw_validation_res_validate(struct vmw_validation_context *ctx, bool intr);
+
+int vmw_validation_prepare(struct vmw_validation_context *ctx,
+                          struct mutex *mutex, bool intr);
+void vmw_validation_revert(struct vmw_validation_context *ctx);
+void vmw_validation_done(struct vmw_validation_context *ctx,
+                        struct vmw_fence_obj *fence);
+
+#endif