diff options
Diffstat (limited to 'drivers/gpu/drm/vmwgfx/vmwgfx_resource.c')
| -rw-r--r-- | drivers/gpu/drm/vmwgfx/vmwgfx_resource.c | 1257 |
1 files changed, 440 insertions, 817 deletions
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c index a96f90f017d1..388011696941 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c @@ -1,7 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT /************************************************************************** * - * Copyright © 2009-2015 VMware, Inc., Palo Alto, CA., USA - * All Rights Reserved. + * Copyright (c) 2009-2024 Broadcom. All Rights Reserved. The term + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the @@ -25,37 +26,58 @@ * **************************************************************************/ -#include "vmwgfx_drv.h" -#include <drm/vmwgfx_drm.h> -#include <drm/ttm/ttm_object.h> #include <drm/ttm/ttm_placement.h> -#include <drm/drmP.h> -#include "vmwgfx_resource_priv.h" + #include "vmwgfx_binding.h" +#include "vmwgfx_bo.h" +#include "vmwgfx_drv.h" +#include "vmwgfx_resource_priv.h" #define VMW_RES_EVICT_ERR_COUNT 10 -struct vmw_user_dma_buffer { - struct ttm_prime_object prime; - struct vmw_dma_buffer dma; -}; +/** + * vmw_resource_mob_attach - Mark a resource as attached to its backing mob + * @res: The resource + */ +void vmw_resource_mob_attach(struct vmw_resource *res) +{ + struct vmw_bo *gbo = res->guest_memory_bo; + struct rb_node **new = &gbo->res_tree.rb_node, *parent = NULL; -struct vmw_bo_user_rep { - uint32_t handle; - uint64_t map_handle; -}; + dma_resv_assert_held(gbo->tbo.base.resv); + res->used_prio = (res->res_dirty) ? res->func->dirty_prio : + res->func->prio; -static inline struct vmw_dma_buffer * -vmw_dma_buffer(struct ttm_buffer_object *bo) -{ - return container_of(bo, struct vmw_dma_buffer, base); + while (*new) { + struct vmw_resource *this = + container_of(*new, struct vmw_resource, mob_node); + + parent = *new; + new = (res->guest_memory_offset < this->guest_memory_offset) ? + &((*new)->rb_left) : &((*new)->rb_right); + } + + rb_link_node(&res->mob_node, parent, new); + rb_insert_color(&res->mob_node, &gbo->res_tree); + vmw_bo_del_detached_resource(gbo, res); + + vmw_bo_prio_add(gbo, res->used_prio); } -static inline struct vmw_user_dma_buffer * -vmw_user_dma_buffer(struct ttm_buffer_object *bo) +/** + * vmw_resource_mob_detach - Mark a resource as detached from its backing mob + * @res: The resource + */ +void vmw_resource_mob_detach(struct vmw_resource *res) { - struct vmw_dma_buffer *vmw_bo = vmw_dma_buffer(bo); - return container_of(vmw_bo, struct vmw_user_dma_buffer, dma); + struct vmw_bo *gbo = res->guest_memory_bo; + + dma_resv_assert_held(gbo->tbo.base.resv); + if (vmw_resource_mob_attached(res)) { + rb_erase(&res->mob_node, &gbo->res_tree); + RB_CLEAR_NODE(&res->mob_node); + vmw_bo_prio_del(gbo, res->used_prio); + } } struct vmw_resource *vmw_resource_reference(struct vmw_resource *res) @@ -82,11 +104,11 @@ void vmw_resource_release_id(struct vmw_resource *res) struct vmw_private *dev_priv = res->dev_priv; struct idr *idr = &dev_priv->res_idr[res->func->res_type]; - write_lock(&dev_priv->resource_lock); + spin_lock(&dev_priv->resource_lock); if (res->id != -1) idr_remove(idr, res->id); res->id = -1; - write_unlock(&dev_priv->resource_lock); + spin_unlock(&dev_priv->resource_lock); } static void vmw_resource_release(struct kref *kref) @@ -95,28 +117,33 @@ static void vmw_resource_release(struct kref *kref) container_of(kref, struct vmw_resource, kref); struct vmw_private *dev_priv = res->dev_priv; int id; + int ret; struct idr *idr = &dev_priv->res_idr[res->func->res_type]; - write_lock(&dev_priv->resource_lock); - res->avail = false; + spin_lock(&dev_priv->resource_lock); list_del_init(&res->lru_head); - write_unlock(&dev_priv->resource_lock); - if (res->backup) { - struct ttm_buffer_object *bo = &res->backup->base; + spin_unlock(&dev_priv->resource_lock); + if (res->guest_memory_bo) { + struct ttm_buffer_object *bo = &res->guest_memory_bo->tbo; - ttm_bo_reserve(bo, false, false, NULL); - if (!list_empty(&res->mob_head) && + ret = ttm_bo_reserve(bo, false, false, NULL); + BUG_ON(ret); + if (vmw_resource_mob_attached(res) && res->func->unbind != NULL) { struct ttm_validate_buffer val_buf; val_buf.bo = bo; - val_buf.shared = false; + val_buf.num_shared = 0; res->func->unbind(res, false, &val_buf); } - res->backup_dirty = false; - list_del_init(&res->mob_head); + res->guest_memory_size = false; + vmw_resource_mob_detach(res); + if (res->dirty) + res->func->dirty_free(res); + if (res->coherent) + vmw_bo_dirty_release(res->guest_memory_bo); ttm_bo_unreserve(bo); - vmw_dmabuf_unreference(&res->backup); + vmw_user_bo_unref(&res->guest_memory_bo); } if (likely(res->hw_destroy != NULL)) { @@ -132,10 +159,10 @@ static void vmw_resource_release(struct kref *kref) else kfree(res); - write_lock(&dev_priv->resource_lock); + spin_lock(&dev_priv->resource_lock); if (id != -1) idr_remove(idr, id); - write_unlock(&dev_priv->resource_lock); + spin_unlock(&dev_priv->resource_lock); } void vmw_resource_unreference(struct vmw_resource **p_res) @@ -164,13 +191,13 @@ int vmw_resource_alloc_id(struct vmw_resource *res) BUG_ON(res->id != -1); idr_preload(GFP_KERNEL); - write_lock(&dev_priv->resource_lock); + spin_lock(&dev_priv->resource_lock); ret = idr_alloc(idr, res, 1, 0, GFP_NOWAIT); if (ret >= 0) res->id = ret; - write_unlock(&dev_priv->resource_lock); + spin_unlock(&dev_priv->resource_lock); idr_preload_end(); return ret < 0 ? ret : 0; } @@ -180,7 +207,6 @@ int vmw_resource_alloc_id(struct vmw_resource *res) * * @dev_priv: Pointer to a device private struct. * @res: The struct vmw_resource to initialize. - * @obj_type: Resource object type. * @delay_id: Boolean whether to defer device id allocation until * the first validation. * @res_free: Resource destructor. @@ -194,45 +220,25 @@ int vmw_resource_init(struct vmw_private *dev_priv, struct vmw_resource *res, kref_init(&res->kref); res->hw_destroy = NULL; res->res_free = res_free; - res->avail = false; res->dev_priv = dev_priv; res->func = func; + RB_CLEAR_NODE(&res->mob_node); INIT_LIST_HEAD(&res->lru_head); - INIT_LIST_HEAD(&res->mob_head); INIT_LIST_HEAD(&res->binding_head); res->id = -1; - res->backup = NULL; - res->backup_offset = 0; - res->backup_dirty = false; + res->guest_memory_bo = NULL; + res->guest_memory_offset = 0; + res->guest_memory_dirty = false; res->res_dirty = false; + res->coherent = false; + res->used_prio = 3; + res->dirty = NULL; if (delay_id) return 0; else return vmw_resource_alloc_id(res); } -/** - * vmw_resource_activate - * - * @res: Pointer to the newly created resource - * @hw_destroy: Destroy function. NULL if none. - * - * Activate a resource after the hardware has been made aware of it. - * Set tye destroy function to @destroy. Typically this frees the - * resource and destroys the hardware resources associated with it. - * Activate basically means that the function vmw_resource_lookup will - * find it. - */ -void vmw_resource_activate(struct vmw_resource *res, - void (*hw_destroy) (struct vmw_resource *)) -{ - struct vmw_private *dev_priv = res->dev_priv; - - write_lock(&dev_priv->resource_lock); - res->avail = true; - res->hw_destroy = hw_destroy; - write_unlock(&dev_priv->resource_lock); -} /** * vmw_user_resource_lookup_handle - lookup a struct resource from a @@ -260,22 +266,14 @@ int vmw_user_resource_lookup_handle(struct vmw_private *dev_priv, int ret = -EINVAL; base = ttm_base_object_lookup(tfile, handle); - if (unlikely(base == NULL)) + if (unlikely(!base)) return -EINVAL; if (unlikely(ttm_base_object_type(base) != converter->object_type)) goto out_bad_resource; res = converter->base_obj_to_res(base); - - read_lock(&dev_priv->resource_lock); - if (!res->avail || res->res_free != converter->res_free) { - read_unlock(&dev_priv->resource_lock); - goto out_bad_resource; - } - - kref_get(&res->kref); - read_unlock(&dev_priv->resource_lock); + vmw_resource_reference(res); *p_res = res; ret = 0; @@ -286,569 +284,76 @@ out_bad_resource: return ret; } -/** - * Helper function that looks either a surface or dmabuf. +/* + * Helper function that looks either a surface or bo. * * The pointer this pointed at by out_surf and out_buf needs to be null. */ -int vmw_user_lookup_handle(struct vmw_private *dev_priv, - struct ttm_object_file *tfile, - uint32_t handle, - struct vmw_surface **out_surf, - struct vmw_dma_buffer **out_buf) +int vmw_user_object_lookup(struct vmw_private *dev_priv, + struct drm_file *filp, + u32 handle, + struct vmw_user_object *uo) { + struct ttm_object_file *tfile = vmw_fpriv(filp)->tfile; struct vmw_resource *res; int ret; - BUG_ON(*out_surf || *out_buf); + WARN_ON(uo->surface || uo->buffer); ret = vmw_user_resource_lookup_handle(dev_priv, tfile, handle, user_surface_converter, &res); if (!ret) { - *out_surf = vmw_res_to_srf(res); + uo->surface = vmw_res_to_srf(res); return 0; } - *out_surf = NULL; - ret = vmw_user_dmabuf_lookup(tfile, handle, out_buf, NULL); - return ret; -} - -/** - * Buffer management. - */ - -/** - * vmw_dmabuf_acc_size - Calculate the pinned memory usage of buffers - * - * @dev_priv: Pointer to a struct vmw_private identifying the device. - * @size: The requested buffer size. - * @user: Whether this is an ordinary dma buffer or a user dma buffer. - */ -static size_t vmw_dmabuf_acc_size(struct vmw_private *dev_priv, size_t size, - bool user) -{ - static size_t struct_size, user_struct_size; - size_t num_pages = PAGE_ALIGN(size) >> PAGE_SHIFT; - size_t page_array_size = ttm_round_pot(num_pages * sizeof(void *)); - - if (unlikely(struct_size == 0)) { - size_t backend_size = ttm_round_pot(vmw_tt_size); - - struct_size = backend_size + - ttm_round_pot(sizeof(struct vmw_dma_buffer)); - user_struct_size = backend_size + - ttm_round_pot(sizeof(struct vmw_user_dma_buffer)); - } - - if (dev_priv->map_mode == vmw_dma_alloc_coherent) - page_array_size += - ttm_round_pot(num_pages * sizeof(dma_addr_t)); - - return ((user) ? user_struct_size : struct_size) + - page_array_size; -} - -void vmw_dmabuf_bo_free(struct ttm_buffer_object *bo) -{ - struct vmw_dma_buffer *vmw_bo = vmw_dma_buffer(bo); - - kfree(vmw_bo); -} - -static void vmw_user_dmabuf_destroy(struct ttm_buffer_object *bo) -{ - struct vmw_user_dma_buffer *vmw_user_bo = vmw_user_dma_buffer(bo); - - ttm_prime_object_kfree(vmw_user_bo, prime); -} - -int vmw_dmabuf_init(struct vmw_private *dev_priv, - struct vmw_dma_buffer *vmw_bo, - size_t size, struct ttm_placement *placement, - bool interruptible, - void (*bo_free) (struct ttm_buffer_object *bo)) -{ - struct ttm_bo_device *bdev = &dev_priv->bdev; - size_t acc_size; - int ret; - bool user = (bo_free == &vmw_user_dmabuf_destroy); - - BUG_ON(!bo_free && (!user && (bo_free != vmw_dmabuf_bo_free))); - - acc_size = vmw_dmabuf_acc_size(dev_priv, size, user); - memset(vmw_bo, 0, sizeof(*vmw_bo)); - - INIT_LIST_HEAD(&vmw_bo->res_list); - - ret = ttm_bo_init(bdev, &vmw_bo->base, size, - ttm_bo_type_device, placement, - 0, interruptible, - NULL, acc_size, NULL, NULL, bo_free); - return ret; -} - -static void vmw_user_dmabuf_release(struct ttm_base_object **p_base) -{ - struct vmw_user_dma_buffer *vmw_user_bo; - struct ttm_base_object *base = *p_base; - struct ttm_buffer_object *bo; - - *p_base = NULL; - - if (unlikely(base == NULL)) - return; - - vmw_user_bo = container_of(base, struct vmw_user_dma_buffer, - prime.base); - bo = &vmw_user_bo->dma.base; - ttm_bo_unref(&bo); -} - -static void vmw_user_dmabuf_ref_obj_release(struct ttm_base_object *base, - enum ttm_ref_type ref_type) -{ - struct vmw_user_dma_buffer *user_bo; - user_bo = container_of(base, struct vmw_user_dma_buffer, prime.base); - - switch (ref_type) { - case TTM_REF_SYNCCPU_WRITE: - ttm_bo_synccpu_write_release(&user_bo->dma.base); - break; - default: - BUG(); - } -} - -/** - * vmw_user_dmabuf_alloc - Allocate a user dma buffer - * - * @dev_priv: Pointer to a struct device private. - * @tfile: Pointer to a struct ttm_object_file on which to register the user - * object. - * @size: Size of the dma buffer. - * @shareable: Boolean whether the buffer is shareable with other open files. - * @handle: Pointer to where the handle value should be assigned. - * @p_dma_buf: Pointer to where the refcounted struct vmw_dma_buffer pointer - * should be assigned. - */ -int vmw_user_dmabuf_alloc(struct vmw_private *dev_priv, - struct ttm_object_file *tfile, - uint32_t size, - bool shareable, - uint32_t *handle, - struct vmw_dma_buffer **p_dma_buf, - struct ttm_base_object **p_base) -{ - struct vmw_user_dma_buffer *user_bo; - struct ttm_buffer_object *tmp; - int ret; - - user_bo = kzalloc(sizeof(*user_bo), GFP_KERNEL); - if (unlikely(!user_bo)) { - DRM_ERROR("Failed to allocate a buffer.\n"); - return -ENOMEM; - } - - ret = vmw_dmabuf_init(dev_priv, &user_bo->dma, size, - (dev_priv->has_mob) ? - &vmw_sys_placement : - &vmw_vram_sys_placement, true, - &vmw_user_dmabuf_destroy); - if (unlikely(ret != 0)) - return ret; - - tmp = ttm_bo_reference(&user_bo->dma.base); - ret = ttm_prime_object_init(tfile, - size, - &user_bo->prime, - shareable, - ttm_buffer_type, - &vmw_user_dmabuf_release, - &vmw_user_dmabuf_ref_obj_release); - if (unlikely(ret != 0)) { - ttm_bo_unref(&tmp); - goto out_no_base_object; - } - - *p_dma_buf = &user_bo->dma; - if (p_base) { - *p_base = &user_bo->prime.base; - kref_get(&(*p_base)->refcount); - } - *handle = user_bo->prime.base.hash.key; - -out_no_base_object: - return ret; -} - -/** - * vmw_user_dmabuf_verify_access - verify access permissions on this - * buffer object. - * - * @bo: Pointer to the buffer object being accessed - * @tfile: Identifying the caller. - */ -int vmw_user_dmabuf_verify_access(struct ttm_buffer_object *bo, - struct ttm_object_file *tfile) -{ - struct vmw_user_dma_buffer *vmw_user_bo; - - if (unlikely(bo->destroy != vmw_user_dmabuf_destroy)) - return -EPERM; - - vmw_user_bo = vmw_user_dma_buffer(bo); - - /* Check that the caller has opened the object. */ - if (likely(ttm_ref_object_exists(tfile, &vmw_user_bo->prime.base))) - return 0; - - DRM_ERROR("Could not grant buffer access.\n"); - return -EPERM; -} - -/** - * vmw_user_dmabuf_synccpu_grab - Grab a struct vmw_user_dma_buffer for cpu - * access, idling previous GPU operations on the buffer and optionally - * blocking it for further command submissions. - * - * @user_bo: Pointer to the buffer object being grabbed for CPU access - * @tfile: Identifying the caller. - * @flags: Flags indicating how the grab should be performed. - * - * A blocking grab will be automatically released when @tfile is closed. - */ -static int vmw_user_dmabuf_synccpu_grab(struct vmw_user_dma_buffer *user_bo, - struct ttm_object_file *tfile, - uint32_t flags) -{ - struct ttm_buffer_object *bo = &user_bo->dma.base; - bool existed; - int ret; - - if (flags & drm_vmw_synccpu_allow_cs) { - bool nonblock = !!(flags & drm_vmw_synccpu_dontblock); - long lret; - - lret = reservation_object_wait_timeout_rcu(bo->resv, true, true, - nonblock ? 0 : MAX_SCHEDULE_TIMEOUT); - if (!lret) - return -EBUSY; - else if (lret < 0) - return lret; - return 0; - } - - ret = ttm_bo_synccpu_write_grab - (bo, !!(flags & drm_vmw_synccpu_dontblock)); - if (unlikely(ret != 0)) - return ret; - - ret = ttm_ref_object_add(tfile, &user_bo->prime.base, - TTM_REF_SYNCCPU_WRITE, &existed, false); - if (ret != 0 || existed) - ttm_bo_synccpu_write_release(&user_bo->dma.base); - - return ret; -} - -/** - * vmw_user_dmabuf_synccpu_release - Release a previous grab for CPU access, - * and unblock command submission on the buffer if blocked. - * - * @handle: Handle identifying the buffer object. - * @tfile: Identifying the caller. - * @flags: Flags indicating the type of release. - */ -static int vmw_user_dmabuf_synccpu_release(uint32_t handle, - struct ttm_object_file *tfile, - uint32_t flags) -{ - if (!(flags & drm_vmw_synccpu_allow_cs)) - return ttm_ref_object_base_unref(tfile, handle, - TTM_REF_SYNCCPU_WRITE); - - return 0; -} - -/** - * vmw_user_dmabuf_synccpu_release - ioctl function implementing the synccpu - * functionality. - * - * @dev: Identifies the drm device. - * @data: Pointer to the ioctl argument. - * @file_priv: Identifies the caller. - * - * This function checks the ioctl arguments for validity and calls the - * relevant synccpu functions. - */ -int vmw_user_dmabuf_synccpu_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_vmw_synccpu_arg *arg = - (struct drm_vmw_synccpu_arg *) data; - struct vmw_dma_buffer *dma_buf; - struct vmw_user_dma_buffer *user_bo; - struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; - struct ttm_base_object *buffer_base; - int ret; - - if ((arg->flags & (drm_vmw_synccpu_read | drm_vmw_synccpu_write)) == 0 - || (arg->flags & ~(drm_vmw_synccpu_read | drm_vmw_synccpu_write | - drm_vmw_synccpu_dontblock | - drm_vmw_synccpu_allow_cs)) != 0) { - DRM_ERROR("Illegal synccpu flags.\n"); - return -EINVAL; - } - - switch (arg->op) { - case drm_vmw_synccpu_grab: - ret = vmw_user_dmabuf_lookup(tfile, arg->handle, &dma_buf, - &buffer_base); - if (unlikely(ret != 0)) - return ret; - - user_bo = container_of(dma_buf, struct vmw_user_dma_buffer, - dma); - ret = vmw_user_dmabuf_synccpu_grab(user_bo, tfile, arg->flags); - vmw_dmabuf_unreference(&dma_buf); - ttm_base_object_unref(&buffer_base); - if (unlikely(ret != 0 && ret != -ERESTARTSYS && - ret != -EBUSY)) { - DRM_ERROR("Failed synccpu grab on handle 0x%08x.\n", - (unsigned int) arg->handle); - return ret; - } - break; - case drm_vmw_synccpu_release: - ret = vmw_user_dmabuf_synccpu_release(arg->handle, tfile, - arg->flags); - if (unlikely(ret != 0)) { - DRM_ERROR("Failed synccpu release on handle 0x%08x.\n", - (unsigned int) arg->handle); - return ret; - } - break; - default: - DRM_ERROR("Invalid synccpu operation.\n"); - return -EINVAL; - } - - return 0; -} - -int vmw_dmabuf_alloc_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct vmw_private *dev_priv = vmw_priv(dev); - union drm_vmw_alloc_dmabuf_arg *arg = - (union drm_vmw_alloc_dmabuf_arg *)data; - struct drm_vmw_alloc_dmabuf_req *req = &arg->req; - struct drm_vmw_dmabuf_rep *rep = &arg->rep; - struct vmw_dma_buffer *dma_buf; - uint32_t handle; - int ret; - - ret = ttm_read_lock(&dev_priv->reservation_sem, true); - if (unlikely(ret != 0)) - return ret; - - ret = vmw_user_dmabuf_alloc(dev_priv, vmw_fpriv(file_priv)->tfile, - req->size, false, &handle, &dma_buf, - NULL); - if (unlikely(ret != 0)) - goto out_no_dmabuf; - - rep->handle = handle; - rep->map_handle = drm_vma_node_offset_addr(&dma_buf->base.vma_node); - rep->cur_gmr_id = handle; - rep->cur_gmr_offset = 0; - - vmw_dmabuf_unreference(&dma_buf); - -out_no_dmabuf: - ttm_read_unlock(&dev_priv->reservation_sem); - - return ret; -} - -int vmw_dmabuf_unref_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_vmw_unref_dmabuf_arg *arg = - (struct drm_vmw_unref_dmabuf_arg *)data; - - return ttm_ref_object_base_unref(vmw_fpriv(file_priv)->tfile, - arg->handle, - TTM_REF_USAGE); -} - -int vmw_user_dmabuf_lookup(struct ttm_object_file *tfile, - uint32_t handle, struct vmw_dma_buffer **out, - struct ttm_base_object **p_base) -{ - struct vmw_user_dma_buffer *vmw_user_bo; - struct ttm_base_object *base; - - base = ttm_base_object_lookup(tfile, handle); - if (unlikely(base == NULL)) { - pr_err("Invalid buffer object handle 0x%08lx\n", - (unsigned long)handle); - return -ESRCH; - } - - if (unlikely(ttm_base_object_type(base) != ttm_buffer_type)) { - ttm_base_object_unref(&base); - pr_err("Invalid buffer object handle 0x%08lx\n", - (unsigned long)handle); - return -EINVAL; + uo->surface = NULL; + ret = vmw_user_bo_lookup(filp, handle, &uo->buffer); + if (!ret && !uo->buffer->is_dumb) { + uo->surface = vmw_lookup_surface_for_buffer(dev_priv, + uo->buffer, + handle); + if (uo->surface) + vmw_user_bo_unref(&uo->buffer); } - vmw_user_bo = container_of(base, struct vmw_user_dma_buffer, - prime.base); - (void)ttm_bo_reference(&vmw_user_bo->dma.base); - if (p_base) - *p_base = base; - else - ttm_base_object_unref(&base); - *out = &vmw_user_bo->dma; - - return 0; -} - -int vmw_user_dmabuf_reference(struct ttm_object_file *tfile, - struct vmw_dma_buffer *dma_buf, - uint32_t *handle) -{ - struct vmw_user_dma_buffer *user_bo; - - if (dma_buf->base.destroy != vmw_user_dmabuf_destroy) - return -EINVAL; - - user_bo = container_of(dma_buf, struct vmw_user_dma_buffer, dma); - - *handle = user_bo->prime.base.hash.key; - return ttm_ref_object_add(tfile, &user_bo->prime.base, - TTM_REF_USAGE, NULL, false); -} - -/** - * vmw_dumb_create - Create a dumb kms buffer - * - * @file_priv: Pointer to a struct drm_file identifying the caller. - * @dev: Pointer to the drm device. - * @args: Pointer to a struct drm_mode_create_dumb structure - * - * This is a driver callback for the core drm create_dumb functionality. - * Note that this is very similar to the vmw_dmabuf_alloc ioctl, except - * that the arguments have a different format. - */ -int vmw_dumb_create(struct drm_file *file_priv, - struct drm_device *dev, - struct drm_mode_create_dumb *args) -{ - struct vmw_private *dev_priv = vmw_priv(dev); - struct vmw_dma_buffer *dma_buf; - int ret; - - args->pitch = args->width * ((args->bpp + 7) / 8); - args->size = args->pitch * args->height; - - ret = ttm_read_lock(&dev_priv->reservation_sem, true); - if (unlikely(ret != 0)) - return ret; - - ret = vmw_user_dmabuf_alloc(dev_priv, vmw_fpriv(file_priv)->tfile, - args->size, false, &args->handle, - &dma_buf, NULL); - if (unlikely(ret != 0)) - goto out_no_dmabuf; - - vmw_dmabuf_unreference(&dma_buf); -out_no_dmabuf: - ttm_read_unlock(&dev_priv->reservation_sem); return ret; } /** - * vmw_dumb_map_offset - Return the address space offset of a dumb buffer + * vmw_resource_buf_alloc - Allocate a guest memory buffer for a resource. * - * @file_priv: Pointer to a struct drm_file identifying the caller. - * @dev: Pointer to the drm device. - * @handle: Handle identifying the dumb buffer. - * @offset: The address space offset returned. - * - * This is a driver callback for the core drm dumb_map_offset functionality. - */ -int vmw_dumb_map_offset(struct drm_file *file_priv, - struct drm_device *dev, uint32_t handle, - uint64_t *offset) -{ - struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; - struct vmw_dma_buffer *out_buf; - int ret; - - ret = vmw_user_dmabuf_lookup(tfile, handle, &out_buf, NULL); - if (ret != 0) - return -EINVAL; - - *offset = drm_vma_node_offset_addr(&out_buf->base.vma_node); - vmw_dmabuf_unreference(&out_buf); - return 0; -} - -/** - * vmw_dumb_destroy - Destroy a dumb boffer - * - * @file_priv: Pointer to a struct drm_file identifying the caller. - * @dev: Pointer to the drm device. - * @handle: Handle identifying the dumb buffer. - * - * This is a driver callback for the core drm dumb_destroy functionality. - */ -int vmw_dumb_destroy(struct drm_file *file_priv, - struct drm_device *dev, - uint32_t handle) -{ - return ttm_ref_object_base_unref(vmw_fpriv(file_priv)->tfile, - handle, TTM_REF_USAGE); -} - -/** - * vmw_resource_buf_alloc - Allocate a backup buffer for a resource. - * - * @res: The resource for which to allocate a backup buffer. + * @res: The resource for which to allocate a gbo buffer. * @interruptible: Whether any sleeps during allocation should be * performed while interruptible. */ static int vmw_resource_buf_alloc(struct vmw_resource *res, bool interruptible) { - unsigned long size = - (res->backup_size + PAGE_SIZE - 1) & PAGE_MASK; - struct vmw_dma_buffer *backup; + unsigned long size = PFN_ALIGN(res->guest_memory_size); + struct vmw_bo *gbo; + struct vmw_bo_params bo_params = { + .domain = res->func->domain, + .busy_domain = res->func->busy_domain, + .bo_type = ttm_bo_type_device, + .size = res->guest_memory_size, + .pin = false + }; int ret; - if (likely(res->backup)) { - BUG_ON(res->backup->base.num_pages * PAGE_SIZE < size); + if (likely(res->guest_memory_bo)) { + BUG_ON(res->guest_memory_bo->tbo.base.size < size); return 0; } - backup = kzalloc(sizeof(*backup), GFP_KERNEL); - if (unlikely(!backup)) - return -ENOMEM; - - ret = vmw_dmabuf_init(res->dev_priv, backup, res->backup_size, - res->func->backup_placement, - interruptible, - &vmw_dmabuf_bo_free); + ret = vmw_bo_create(res->dev_priv, &bo_params, &gbo); if (unlikely(ret != 0)) - goto out_no_dmabuf; + goto out_no_bo; - res->backup = backup; + res->guest_memory_bo = gbo; -out_no_dmabuf: +out_no_bo: return ret; } @@ -859,12 +364,14 @@ out_no_dmabuf: * @res: The resource to make visible to the device. * @val_buf: Information about a buffer possibly * containing backup data if a bind operation is needed. + * @dirtying: Transfer dirty regions. * * On hardware resource shortage, this function returns -EBUSY and * should be retried once resources have been freed up. */ static int vmw_resource_do_validate(struct vmw_resource *res, - struct ttm_validate_buffer *val_buf) + struct ttm_validate_buffer *val_buf, + bool dirtying) { int ret = 0; const struct vmw_res_func *func = res->func; @@ -876,23 +383,48 @@ static int vmw_resource_do_validate(struct vmw_resource *res, } if (func->bind && - ((func->needs_backup && list_empty(&res->mob_head) && - val_buf->bo != NULL) || - (!func->needs_backup && val_buf->bo != NULL))) { + ((func->needs_guest_memory && !vmw_resource_mob_attached(res) && + val_buf->bo) || + (!func->needs_guest_memory && val_buf->bo))) { ret = func->bind(res, val_buf); if (unlikely(ret != 0)) goto out_bind_failed; - if (func->needs_backup) - list_add_tail(&res->mob_head, &res->backup->res_list); + if (func->needs_guest_memory) + vmw_resource_mob_attach(res); + } + + /* + * Handle the case where the backup mob is marked coherent but + * the resource isn't. + */ + if (func->dirty_alloc && vmw_resource_mob_attached(res) && + !res->coherent) { + if (res->guest_memory_bo->dirty && !res->dirty) { + ret = func->dirty_alloc(res); + if (ret) + return ret; + } else if (!res->guest_memory_bo->dirty && res->dirty) { + func->dirty_free(res); + } } /* - * Only do this on write operations, and move to - * vmw_resource_unreserve if it can be called after - * backup buffers have been unreserved. Otherwise - * sort out locking. + * Transfer the dirty regions to the resource and update + * the resource. */ - res->res_dirty = true; + if (res->dirty) { + if (dirtying && !res->res_dirty) { + pgoff_t start = res->guest_memory_offset >> PAGE_SHIFT; + pgoff_t end = __KERNEL_DIV_ROUND_UP + (res->guest_memory_offset + res->guest_memory_size, + PAGE_SIZE); + + vmw_bo_dirty_unmap(res->guest_memory_bo, start, end); + } + + vmw_bo_dirty_transfer_to_res(res); + return func->dirty_sync(res); + } return 0; @@ -907,49 +439,66 @@ out_bind_failed: * command submission. * * @res: Pointer to the struct vmw_resource to unreserve. - * @switch_backup: Backup buffer has been switched. - * @new_backup: Pointer to new backup buffer if command submission + * @dirty_set: Change dirty status of the resource. + * @dirty: When changing dirty status indicates the new status. + * @switch_guest_memory: Guest memory buffer has been switched. + * @new_guest_memory_bo: Pointer to new guest memory buffer if command submission * switched. May be NULL. - * @new_backup_offset: New backup offset if @switch_backup is true. + * @new_guest_memory_offset: New gbo offset if @switch_guest_memory is true. * * Currently unreserving a resource means putting it back on the device's * resource lru list, so that it can be evicted if necessary. */ void vmw_resource_unreserve(struct vmw_resource *res, - bool switch_backup, - struct vmw_dma_buffer *new_backup, - unsigned long new_backup_offset) + bool dirty_set, + bool dirty, + bool switch_guest_memory, + struct vmw_bo *new_guest_memory_bo, + unsigned long new_guest_memory_offset) { struct vmw_private *dev_priv = res->dev_priv; if (!list_empty(&res->lru_head)) return; - if (switch_backup && new_backup != res->backup) { - if (res->backup) { - lockdep_assert_held(&res->backup->base.resv->lock.base); - list_del_init(&res->mob_head); - vmw_dmabuf_unreference(&res->backup); + if (switch_guest_memory && new_guest_memory_bo != res->guest_memory_bo) { + if (res->guest_memory_bo) { + vmw_resource_mob_detach(res); + if (res->coherent) + vmw_bo_dirty_release(res->guest_memory_bo); + vmw_user_bo_unref(&res->guest_memory_bo); } - if (new_backup) { - res->backup = vmw_dmabuf_reference(new_backup); - lockdep_assert_held(&new_backup->base.resv->lock.base); - list_add_tail(&res->mob_head, &new_backup->res_list); + if (new_guest_memory_bo) { + res->guest_memory_bo = vmw_user_bo_ref(new_guest_memory_bo); + + /* + * The validation code should already have added a + * dirty tracker here. + */ + WARN_ON(res->coherent && !new_guest_memory_bo->dirty); + + vmw_resource_mob_attach(res); } else { - res->backup = NULL; + res->guest_memory_bo = NULL; } + } else if (switch_guest_memory && res->coherent) { + vmw_bo_dirty_release(res->guest_memory_bo); } - if (switch_backup) - res->backup_offset = new_backup_offset; + + if (switch_guest_memory) + res->guest_memory_offset = new_guest_memory_offset; + + if (dirty_set) + res->res_dirty = dirty; if (!res->func->may_evict || res->id == -1 || res->pin_count) return; - write_lock(&dev_priv->resource_lock); + spin_lock(&dev_priv->resource_lock); list_add_tail(&res->lru_head, &res->dev_priv->res_lru[res->func->res_type]); - write_unlock(&dev_priv->resource_lock); + spin_unlock(&dev_priv->resource_lock); } /** @@ -957,6 +506,7 @@ void vmw_resource_unreserve(struct vmw_resource *res, * for a resource and in that case, allocate * one, reserve and validate it. * + * @ticket: The ww acquire context to use, or NULL if trylocking. * @res: The resource for which to allocate a backup buffer. * @interruptible: Whether any sleeps during allocation should be * performed while interruptible. @@ -964,35 +514,40 @@ void vmw_resource_unreserve(struct vmw_resource *res, * reserved and validated backup buffer. */ static int -vmw_resource_check_buffer(struct vmw_resource *res, +vmw_resource_check_buffer(struct ww_acquire_ctx *ticket, + struct vmw_resource *res, bool interruptible, struct ttm_validate_buffer *val_buf) { + struct ttm_operation_ctx ctx = { true, false }; struct list_head val_list; - bool backup_dirty = false; + bool guest_memory_dirty = false; int ret; - if (unlikely(res->backup == NULL)) { + if (unlikely(!res->guest_memory_bo)) { ret = vmw_resource_buf_alloc(res, interruptible); if (unlikely(ret != 0)) return ret; } INIT_LIST_HEAD(&val_list); - val_buf->bo = ttm_bo_reference(&res->backup->base); - val_buf->shared = false; + val_buf->bo = &res->guest_memory_bo->tbo; + val_buf->num_shared = 0; + drm_gem_object_get(&val_buf->bo->base); list_add_tail(&val_buf->head, &val_list); - ret = ttm_eu_reserve_buffers(NULL, &val_list, interruptible, NULL); + ret = ttm_eu_reserve_buffers(ticket, &val_list, interruptible, NULL); if (unlikely(ret != 0)) goto out_no_reserve; - if (res->func->needs_backup && list_empty(&res->mob_head)) + if (res->func->needs_guest_memory && !vmw_resource_mob_attached(res)) return 0; - backup_dirty = res->backup_dirty; - ret = ttm_bo_validate(&res->backup->base, - res->func->backup_placement, - true, false); + guest_memory_dirty = res->guest_memory_dirty; + vmw_bo_placement_set(res->guest_memory_bo, res->func->domain, + res->func->busy_domain); + ret = ttm_bo_validate(&res->guest_memory_bo->tbo, + &res->guest_memory_bo->placement, + &ctx); if (unlikely(ret != 0)) goto out_no_validate; @@ -1000,42 +555,44 @@ vmw_resource_check_buffer(struct vmw_resource *res, return 0; out_no_validate: - ttm_eu_backoff_reservation(NULL, &val_list); + ttm_eu_backoff_reservation(ticket, &val_list); out_no_reserve: - ttm_bo_unref(&val_buf->bo); - if (backup_dirty) - vmw_dmabuf_unreference(&res->backup); + drm_gem_object_put(&val_buf->bo->base); + val_buf->bo = NULL; + if (guest_memory_dirty) + vmw_user_bo_unref(&res->guest_memory_bo); return ret; } -/** +/* * vmw_resource_reserve - Reserve a resource for command submission * * @res: The resource to reserve. * * This function takes the resource off the LRU list and make sure - * a backup buffer is present for guest-backed resources. However, - * the buffer may not be bound to the resource at this point. + * a guest memory buffer is present for guest-backed resources. + * However, the buffer may not be bound to the resource at this + * point. * */ int vmw_resource_reserve(struct vmw_resource *res, bool interruptible, - bool no_backup) + bool no_guest_memory) { struct vmw_private *dev_priv = res->dev_priv; int ret; - write_lock(&dev_priv->resource_lock); + spin_lock(&dev_priv->resource_lock); list_del_init(&res->lru_head); - write_unlock(&dev_priv->resource_lock); + spin_unlock(&dev_priv->resource_lock); - if (res->func->needs_backup && res->backup == NULL && - !no_backup) { + if (res->func->needs_guest_memory && !res->guest_memory_bo && + !no_guest_memory) { ret = vmw_resource_buf_alloc(res, interruptible); if (unlikely(ret != 0)) { - DRM_ERROR("Failed to allocate a backup buffer " + DRM_ERROR("Failed to allocate a guest memory buffer " "of size %lu. bytes\n", - (unsigned long) res->backup_size); + (unsigned long) res->guest_memory_size); return ret; } } @@ -1045,12 +602,14 @@ int vmw_resource_reserve(struct vmw_resource *res, bool interruptible, /** * vmw_resource_backoff_reservation - Unreserve and unreference a - * backup buffer + * guest memory buffer *. - * @val_buf: Backup buffer information. + * @ticket: The ww acquire ctx used for reservation. + * @val_buf: Guest memory buffer information. */ static void -vmw_resource_backoff_reservation(struct ttm_validate_buffer *val_buf) +vmw_resource_backoff_reservation(struct ww_acquire_ctx *ticket, + struct ttm_validate_buffer *val_buf) { struct list_head val_list; @@ -1059,18 +618,21 @@ vmw_resource_backoff_reservation(struct ttm_validate_buffer *val_buf) INIT_LIST_HEAD(&val_list); list_add_tail(&val_buf->head, &val_list); - ttm_eu_backoff_reservation(NULL, &val_list); - ttm_bo_unref(&val_buf->bo); + ttm_eu_backoff_reservation(ticket, &val_list); + drm_gem_object_put(&val_buf->bo->base); + val_buf->bo = NULL; } /** * vmw_resource_do_evict - Evict a resource, and transfer its data * to a backup buffer. * + * @ticket: The ww acquire ticket to use, or NULL if trylocking. * @res: The resource to evict. * @interruptible: Whether to wait interruptible. */ -static int vmw_resource_do_evict(struct vmw_resource *res, bool interruptible) +static int vmw_resource_do_evict(struct ww_acquire_ctx *ticket, + struct vmw_resource *res, bool interruptible) { struct ttm_validate_buffer val_buf; const struct vmw_res_func *func = res->func; @@ -1079,23 +641,23 @@ static int vmw_resource_do_evict(struct vmw_resource *res, bool interruptible) BUG_ON(!func->may_evict); val_buf.bo = NULL; - val_buf.shared = false; - ret = vmw_resource_check_buffer(res, interruptible, &val_buf); + val_buf.num_shared = 0; + ret = vmw_resource_check_buffer(ticket, res, interruptible, &val_buf); if (unlikely(ret != 0)) return ret; if (unlikely(func->unbind != NULL && - (!func->needs_backup || !list_empty(&res->mob_head)))) { + (!func->needs_guest_memory || vmw_resource_mob_attached(res)))) { ret = func->unbind(res, res->res_dirty, &val_buf); if (unlikely(ret != 0)) goto out_no_unbind; - list_del_init(&res->mob_head); + vmw_resource_mob_detach(res); } ret = func->destroy(res); - res->backup_dirty = true; + res->guest_memory_dirty = true; res->res_dirty = false; out_no_unbind: - vmw_resource_backoff_reservation(&val_buf); + vmw_resource_backoff_reservation(ticket, &val_buf); return ret; } @@ -1104,15 +666,20 @@ out_no_unbind: /** * vmw_resource_validate - Make a resource up-to-date and visible * to the device. + * @res: The resource to make visible to the device. + * @intr: Perform waits interruptible if possible. + * @dirtying: Pending GPU operation will dirty the resource * - * @res: The resource to make visible to the device. - * - * On succesful return, any backup DMA buffer pointed to by @res->backup will + * On successful return, any backup DMA buffer pointed to by @res->backup will * be reserved and validated. * On hardware resource shortage, this function will repeatedly evict * resources of the same type until the validation succeeds. + * + * Return: Zero on success, -ERESTARTSYS if interrupted, negative error code + * on failure. */ -int vmw_resource_validate(struct vmw_resource *res) +int vmw_resource_validate(struct vmw_resource *res, bool intr, + bool dirtying) { int ret; struct vmw_resource *evict_res; @@ -1125,20 +692,20 @@ int vmw_resource_validate(struct vmw_resource *res) return 0; val_buf.bo = NULL; - val_buf.shared = false; - if (res->backup) - val_buf.bo = &res->backup->base; + val_buf.num_shared = 0; + if (res->guest_memory_bo) + val_buf.bo = &res->guest_memory_bo->tbo; do { - ret = vmw_resource_do_validate(res, &val_buf); + ret = vmw_resource_do_validate(res, &val_buf, dirtying); if (likely(ret != -EBUSY)) break; - write_lock(&dev_priv->resource_lock); + spin_lock(&dev_priv->resource_lock); if (list_empty(lru_list) || !res->func->may_evict) { DRM_ERROR("Out of device device resources " "for %s.\n", res->func->type_name); ret = -EBUSY; - write_unlock(&dev_priv->resource_lock); + spin_unlock(&dev_priv->resource_lock); break; } @@ -1147,13 +714,14 @@ int vmw_resource_validate(struct vmw_resource *res) lru_head)); list_del_init(&evict_res->lru_head); - write_unlock(&dev_priv->resource_lock); + spin_unlock(&dev_priv->resource_lock); - ret = vmw_resource_do_evict(evict_res, true); + /* Trylock backup buffers with a NULL ticket. */ + ret = vmw_resource_do_evict(NULL, evict_res, intr); if (unlikely(ret != 0)) { - write_lock(&dev_priv->resource_lock); + spin_lock(&dev_priv->resource_lock); list_add_tail(&evict_res->lru_head, lru_list); - write_unlock(&dev_priv->resource_lock); + spin_unlock(&dev_priv->resource_lock); if (ret == -ERESTARTSYS || ++err_count > VMW_RES_EVICT_ERR_COUNT) { vmw_resource_unreference(&evict_res); @@ -1166,9 +734,9 @@ int vmw_resource_validate(struct vmw_resource *res) if (unlikely(ret != 0)) goto out_no_validate; - else if (!res->func->needs_backup && res->backup) { - list_del_init(&res->mob_head); - vmw_dmabuf_unreference(&res->backup); + else if (!res->func->needs_guest_memory && res->guest_memory_bo) { + WARN_ON_ONCE(vmw_resource_mob_attached(res)); + vmw_user_bo_unref(&res->guest_memory_bo); } return 0; @@ -1177,89 +745,41 @@ out_no_validate: return ret; } -/** - * vmw_fence_single_bo - Utility function to fence a single TTM buffer - * object without unreserving it. - * - * @bo: Pointer to the struct ttm_buffer_object to fence. - * @fence: Pointer to the fence. If NULL, this function will - * insert a fence into the command stream.. - * - * Contrary to the ttm_eu version of this function, it takes only - * a single buffer object instead of a list, and it also doesn't - * unreserve the buffer object, which needs to be done separately. - */ -void vmw_fence_single_bo(struct ttm_buffer_object *bo, - struct vmw_fence_obj *fence) -{ - struct ttm_bo_device *bdev = bo->bdev; - - struct vmw_private *dev_priv = - container_of(bdev, struct vmw_private, bdev); - - if (fence == NULL) { - vmw_execbuf_fence_commands(NULL, dev_priv, &fence, NULL); - reservation_object_add_excl_fence(bo->resv, &fence->base); - dma_fence_put(&fence->base); - } else - reservation_object_add_excl_fence(bo->resv, &fence->base); -} /** - * vmw_resource_move_notify - TTM move_notify_callback + * vmw_resource_unbind_list * - * @bo: The TTM buffer object about to move. - * @mem: The struct ttm_mem_reg indicating to what memory - * region the move is taking place. + * @vbo: Pointer to the current backing MOB. * * Evicts the Guest Backed hardware resource if the backup * buffer is being moved out of MOB memory. - * Note that this function should not race with the resource - * validation code as long as it accesses only members of struct - * resource that remain static while bo::res is !NULL and - * while we have @bo reserved. struct resource::backup is *not* a - * static member. The resource validation code will take care - * to set @bo::res to NULL, while having @bo reserved when the - * buffer is no longer bound to the resource, so @bo:res can be - * used to determine whether there is a need to unbind and whether - * it is safe to unbind. + * Note that this function will not race with the resource + * validation code, since resource validation and eviction + * both require the backup buffer to be reserved. */ -void vmw_resource_move_notify(struct ttm_buffer_object *bo, - struct ttm_mem_reg *mem) +void vmw_resource_unbind_list(struct vmw_bo *vbo) { - struct vmw_dma_buffer *dma_buf; - - if (mem == NULL) - return; - - if (bo->destroy != vmw_dmabuf_bo_free && - bo->destroy != vmw_user_dmabuf_destroy) - return; - - dma_buf = container_of(bo, struct vmw_dma_buffer, base); - - if (mem->mem_type != VMW_PL_MOB) { - struct vmw_resource *res, *n; - struct ttm_validate_buffer val_buf; - - val_buf.bo = bo; - val_buf.shared = false; - - list_for_each_entry_safe(res, n, &dma_buf->res_list, mob_head) { - - if (unlikely(res->func->unbind == NULL)) - continue; - - (void) res->func->unbind(res, true, &val_buf); - res->backup_dirty = true; - res->res_dirty = false; - list_del_init(&res->mob_head); - } - - (void) ttm_bo_wait(bo, false, false); + struct ttm_validate_buffer val_buf = { + .bo = &vbo->tbo, + .num_shared = 0 + }; + + dma_resv_assert_held(vbo->tbo.base.resv); + while (!RB_EMPTY_ROOT(&vbo->res_tree)) { + struct rb_node *node = vbo->res_tree.rb_node; + struct vmw_resource *res = + container_of(node, struct vmw_resource, mob_node); + + if (!WARN_ON_ONCE(!res->func->unbind)) + (void) res->func->unbind(res, res->res_dirty, &val_buf); + + res->guest_memory_size = true; + res->res_dirty = false; + vmw_resource_mob_detach(res); } -} + (void) ttm_bo_wait(&vbo->tbo, false, false); +} /** @@ -1268,9 +788,9 @@ void vmw_resource_move_notify(struct ttm_buffer_object *bo, * @dx_query_mob: Buffer containing the DX query MOB * * Read back cached states from the device if they exist. This function - * assumings binding_mutex is held. + * assumes binding_mutex is held. */ -int vmw_query_readback_all(struct vmw_dma_buffer *dx_query_mob) +int vmw_query_readback_all(struct vmw_bo *dx_query_mob) { struct vmw_resource *dx_query_ctx; struct vmw_private *dev_priv; @@ -1287,18 +807,15 @@ int vmw_query_readback_all(struct vmw_dma_buffer *dx_query_mob) dx_query_ctx = dx_query_mob->dx_query_ctx; dev_priv = dx_query_ctx->dev_priv; - cmd = vmw_fifo_reserve_dx(dev_priv, sizeof(*cmd), dx_query_ctx->id); - if (unlikely(cmd == NULL)) { - DRM_ERROR("Failed reserving FIFO space for " - "query MOB read back.\n"); + cmd = VMW_CMD_CTX_RESERVE(dev_priv, sizeof(*cmd), dx_query_ctx->id); + if (unlikely(cmd == NULL)) return -ENOMEM; - } cmd->header.id = SVGA_3D_CMD_DX_READBACK_ALL_QUERY; cmd->header.size = sizeof(cmd->body); cmd->body.cid = dx_query_ctx->id; - vmw_fifo_commit(dev_priv, sizeof(*cmd)); + vmw_cmd_commit(dev_priv, sizeof(*cmd)); /* Triggers a rebind the next time affected context is bound */ dx_query_mob->dx_query_ctx = NULL; @@ -1312,39 +829,40 @@ int vmw_query_readback_all(struct vmw_dma_buffer *dx_query_mob) * vmw_query_move_notify - Read back cached query states * * @bo: The TTM buffer object about to move. - * @mem: The memory region @bo is moving to. + * @old_mem: The memory region @bo is moving from. + * @new_mem: The memory region @bo is moving to. * * Called before the query MOB is swapped out to read back cached query * states from the device. */ void vmw_query_move_notify(struct ttm_buffer_object *bo, - struct ttm_mem_reg *mem) + struct ttm_resource *old_mem, + struct ttm_resource *new_mem) { - struct vmw_dma_buffer *dx_query_mob; - struct ttm_bo_device *bdev = bo->bdev; - struct vmw_private *dev_priv; - - - dev_priv = container_of(bdev, struct vmw_private, bdev); + struct vmw_bo *dx_query_mob; + struct ttm_device *bdev = bo->bdev; + struct vmw_private *dev_priv = vmw_priv_from_ttm(bdev); mutex_lock(&dev_priv->binding_mutex); - dx_query_mob = container_of(bo, struct vmw_dma_buffer, base); - if (mem == NULL || !dx_query_mob || !dx_query_mob->dx_query_ctx) { - mutex_unlock(&dev_priv->binding_mutex); - return; - } - /* If BO is being moved from MOB to system memory */ - if (mem->mem_type == TTM_PL_SYSTEM && bo->mem.mem_type == VMW_PL_MOB) { + if (old_mem && + new_mem->mem_type == TTM_PL_SYSTEM && + old_mem->mem_type == VMW_PL_MOB) { struct vmw_fence_obj *fence; + dx_query_mob = to_vmw_bo(&bo->base); + if (!dx_query_mob || !dx_query_mob->dx_query_ctx) { + mutex_unlock(&dev_priv->binding_mutex); + return; + } + (void) vmw_query_readback_all(dx_query_mob); mutex_unlock(&dev_priv->binding_mutex); /* Create a fence and attach the BO to it */ (void) vmw_execbuf_fence_commands(NULL, dev_priv, &fence, NULL); - vmw_fence_single_bo(bo, fence); + vmw_bo_fence_single(bo, fence); if (fence != NULL) vmw_fence_obj_unreference(&fence); @@ -1352,7 +870,6 @@ void vmw_query_move_notify(struct ttm_buffer_object *bo, (void) ttm_bo_wait(bo, false, false); } else mutex_unlock(&dev_priv->binding_mutex); - } /** @@ -1362,7 +879,7 @@ void vmw_query_move_notify(struct ttm_buffer_object *bo, */ bool vmw_resource_needs_backup(const struct vmw_resource *res) { - return res->func->needs_backup; + return res->func->needs_guest_memory; } /** @@ -1381,9 +898,10 @@ static void vmw_resource_evict_type(struct vmw_private *dev_priv, struct vmw_resource *evict_res; unsigned err_count = 0; int ret; + struct ww_acquire_ctx ticket; do { - write_lock(&dev_priv->resource_lock); + spin_lock(&dev_priv->resource_lock); if (list_empty(lru_list)) goto out_unlock; @@ -1392,13 +910,14 @@ static void vmw_resource_evict_type(struct vmw_private *dev_priv, list_first_entry(lru_list, struct vmw_resource, lru_head)); list_del_init(&evict_res->lru_head); - write_unlock(&dev_priv->resource_lock); + spin_unlock(&dev_priv->resource_lock); - ret = vmw_resource_do_evict(evict_res, false); + /* Wait lock backup buffers with a ticket. */ + ret = vmw_resource_do_evict(&ticket, evict_res, false); if (unlikely(ret != 0)) { - write_lock(&dev_priv->resource_lock); + spin_lock(&dev_priv->resource_lock); list_add_tail(&evict_res->lru_head, lru_list); - write_unlock(&dev_priv->resource_lock); + spin_unlock(&dev_priv->resource_lock); if (++err_count > VMW_RES_EVICT_ERR_COUNT) { vmw_resource_unreference(&evict_res); return; @@ -1409,7 +928,7 @@ static void vmw_resource_evict_type(struct vmw_private *dev_priv, } while (1); out_unlock: - write_unlock(&dev_priv->resource_lock); + spin_unlock(&dev_priv->resource_lock); } /** @@ -1434,7 +953,7 @@ void vmw_resource_evict_all(struct vmw_private *dev_priv) mutex_unlock(&dev_priv->cmdbuf_mutex); } -/** +/* * vmw_resource_pin - Add a pin reference on a resource * * @res: The resource to add a pin reference on @@ -1446,29 +965,34 @@ void vmw_resource_evict_all(struct vmw_private *dev_priv) */ int vmw_resource_pin(struct vmw_resource *res, bool interruptible) { + struct ttm_operation_ctx ctx = { interruptible, false }; struct vmw_private *dev_priv = res->dev_priv; int ret; - ttm_write_lock(&dev_priv->reservation_sem, interruptible); mutex_lock(&dev_priv->cmdbuf_mutex); ret = vmw_resource_reserve(res, interruptible, false); if (ret) goto out_no_reserve; if (res->pin_count == 0) { - struct vmw_dma_buffer *vbo = NULL; + struct vmw_bo *vbo = NULL; - if (res->backup) { - vbo = res->backup; + if (res->guest_memory_bo) { + vbo = res->guest_memory_bo; - ttm_bo_reserve(&vbo->base, interruptible, false, NULL); - if (!vbo->pin_count) { + ret = ttm_bo_reserve(&vbo->tbo, interruptible, false, NULL); + if (ret) + goto out_no_validate; + if (!vbo->tbo.pin_count) { + vmw_bo_placement_set(vbo, + res->func->domain, + res->func->busy_domain); ret = ttm_bo_validate - (&vbo->base, - res->func->backup_placement, - interruptible, false); + (&vbo->tbo, + &vbo->placement, + &ctx); if (ret) { - ttm_bo_unreserve(&vbo->base); + ttm_bo_unreserve(&vbo->tbo); goto out_no_validate; } } @@ -1476,19 +1000,18 @@ int vmw_resource_pin(struct vmw_resource *res, bool interruptible) /* Do we really need to pin the MOB as well? */ vmw_bo_pin_reserved(vbo, true); } - ret = vmw_resource_validate(res); + ret = vmw_resource_validate(res, interruptible, true); if (vbo) - ttm_bo_unreserve(&vbo->base); + ttm_bo_unreserve(&vbo->tbo); if (ret) goto out_no_validate; } res->pin_count++; out_no_validate: - vmw_resource_unreserve(res, false, NULL, 0UL); + vmw_resource_unreserve(res, false, false, false, NULL, 0UL); out_no_reserve: mutex_unlock(&dev_priv->cmdbuf_mutex); - ttm_write_unlock(&dev_priv->reservation_sem); return ret; } @@ -1506,25 +1029,23 @@ void vmw_resource_unpin(struct vmw_resource *res) struct vmw_private *dev_priv = res->dev_priv; int ret; - (void) ttm_read_lock(&dev_priv->reservation_sem, false); mutex_lock(&dev_priv->cmdbuf_mutex); ret = vmw_resource_reserve(res, false, true); WARN_ON(ret); WARN_ON(res->pin_count == 0); - if (--res->pin_count == 0 && res->backup) { - struct vmw_dma_buffer *vbo = res->backup; + if (--res->pin_count == 0 && res->guest_memory_bo) { + struct vmw_bo *vbo = res->guest_memory_bo; - (void) ttm_bo_reserve(&vbo->base, false, false, NULL); + (void) ttm_bo_reserve(&vbo->tbo, false, false, NULL); vmw_bo_pin_reserved(vbo, false); - ttm_bo_unreserve(&vbo->base); + ttm_bo_unreserve(&vbo->tbo); } - vmw_resource_unreserve(res, false, NULL, 0UL); + vmw_resource_unreserve(res, false, false, false, NULL, 0UL); mutex_unlock(&dev_priv->cmdbuf_mutex); - ttm_read_unlock(&dev_priv->reservation_sem); } /** @@ -1536,3 +1057,105 @@ enum vmw_res_type vmw_res_type(const struct vmw_resource *res) { return res->func->res_type; } + +/** + * vmw_resource_dirty_update - Update a resource's dirty tracker with a + * sequential range of touched backing store memory. + * @res: The resource. + * @start: The first page touched. + * @end: The last page touched + 1. + */ +void vmw_resource_dirty_update(struct vmw_resource *res, pgoff_t start, + pgoff_t end) +{ + if (res->dirty) + res->func->dirty_range_add(res, start << PAGE_SHIFT, + end << PAGE_SHIFT); +} + +int vmw_resource_clean(struct vmw_resource *res) +{ + int ret = 0; + + if (res->res_dirty) { + if (!res->func->clean) + return -EINVAL; + + ret = res->func->clean(res); + if (ret) + return ret; + res->res_dirty = false; + } + return ret; +} + +/** + * vmw_resources_clean - Clean resources intersecting a mob range + * @vbo: The mob buffer object + * @start: The mob page offset starting the range + * @end: The mob page offset ending the range + * @num_prefault: Returns how many pages including the first have been + * cleaned and are ok to prefault + */ +int vmw_resources_clean(struct vmw_bo *vbo, pgoff_t start, + pgoff_t end, pgoff_t *num_prefault) +{ + struct rb_node *cur = vbo->res_tree.rb_node; + struct vmw_resource *found = NULL; + unsigned long res_start = start << PAGE_SHIFT; + unsigned long res_end = end << PAGE_SHIFT; + unsigned long last_cleaned = 0; + int ret; + + /* + * Find the resource with lowest backup_offset that intersects the + * range. + */ + while (cur) { + struct vmw_resource *cur_res = + container_of(cur, struct vmw_resource, mob_node); + + if (cur_res->guest_memory_offset >= res_end) { + cur = cur->rb_left; + } else if (cur_res->guest_memory_offset + cur_res->guest_memory_size <= + res_start) { + cur = cur->rb_right; + } else { + found = cur_res; + cur = cur->rb_left; + /* Continue to look for resources with lower offsets */ + } + } + + /* + * In order of increasing guest_memory_offset, clean dirty resources + * intersecting the range. + */ + while (found) { + ret = vmw_resource_clean(found); + if (ret) + return ret; + last_cleaned = found->guest_memory_offset + found->guest_memory_size; + cur = rb_next(&found->mob_node); + if (!cur) + break; + + found = container_of(cur, struct vmw_resource, mob_node); + if (found->guest_memory_offset >= res_end) + break; + } + + /* + * Set number of pages allowed prefaulting and fence the buffer object + */ + *num_prefault = 1; + if (last_cleaned > res_start) { + struct ttm_buffer_object *bo = &vbo->tbo; + + *num_prefault = __KERNEL_DIV_ROUND_UP(last_cleaned - res_start, + PAGE_SIZE); + vmw_bo_fence_single(bo, NULL); + } + + return 0; +} |
