162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR MIT
262306a36Sopenharmony_ci/**************************************************************************
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright 2009-2023 VMware, Inc., Palo Alto, CA., USA
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
762306a36Sopenharmony_ci * copy of this software and associated documentation files (the
862306a36Sopenharmony_ci * "Software"), to deal in the Software without restriction, including
962306a36Sopenharmony_ci * without limitation the rights to use, copy, modify, merge, publish,
1062306a36Sopenharmony_ci * distribute, sub license, and/or sell copies of the Software, and to
1162306a36Sopenharmony_ci * permit persons to whom the Software is furnished to do so, subject to
1262306a36Sopenharmony_ci * the following conditions:
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci * The above copyright notice and this permission notice (including the
1562306a36Sopenharmony_ci * next paragraph) shall be included in all copies or substantial portions
1662306a36Sopenharmony_ci * of the Software.
1762306a36Sopenharmony_ci *
1862306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1962306a36Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2062306a36Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
2162306a36Sopenharmony_ci * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
2262306a36Sopenharmony_ci * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
2362306a36Sopenharmony_ci * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
2462306a36Sopenharmony_ci * USE OR OTHER DEALINGS IN THE SOFTWARE.
2562306a36Sopenharmony_ci *
2662306a36Sopenharmony_ci **************************************************************************/
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#include <drm/ttm/ttm_placement.h>
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#include "vmwgfx_binding.h"
3162306a36Sopenharmony_ci#include "vmwgfx_bo.h"
3262306a36Sopenharmony_ci#include "vmwgfx_drv.h"
3362306a36Sopenharmony_ci#include "vmwgfx_resource_priv.h"
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#define VMW_RES_EVICT_ERR_COUNT 10
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci/**
3862306a36Sopenharmony_ci * vmw_resource_mob_attach - Mark a resource as attached to its backing mob
3962306a36Sopenharmony_ci * @res: The resource
4062306a36Sopenharmony_ci */
4162306a36Sopenharmony_civoid vmw_resource_mob_attach(struct vmw_resource *res)
4262306a36Sopenharmony_ci{
4362306a36Sopenharmony_ci	struct vmw_bo *gbo = res->guest_memory_bo;
4462306a36Sopenharmony_ci	struct rb_node **new = &gbo->res_tree.rb_node, *parent = NULL;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	dma_resv_assert_held(gbo->tbo.base.resv);
4762306a36Sopenharmony_ci	res->used_prio = (res->res_dirty) ? res->func->dirty_prio :
4862306a36Sopenharmony_ci		res->func->prio;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	while (*new) {
5162306a36Sopenharmony_ci		struct vmw_resource *this =
5262306a36Sopenharmony_ci			container_of(*new, struct vmw_resource, mob_node);
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci		parent = *new;
5562306a36Sopenharmony_ci		new = (res->guest_memory_offset < this->guest_memory_offset) ?
5662306a36Sopenharmony_ci			&((*new)->rb_left) : &((*new)->rb_right);
5762306a36Sopenharmony_ci	}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	rb_link_node(&res->mob_node, parent, new);
6062306a36Sopenharmony_ci	rb_insert_color(&res->mob_node, &gbo->res_tree);
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	vmw_bo_prio_add(gbo, res->used_prio);
6362306a36Sopenharmony_ci}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci/**
6662306a36Sopenharmony_ci * vmw_resource_mob_detach - Mark a resource as detached from its backing mob
6762306a36Sopenharmony_ci * @res: The resource
6862306a36Sopenharmony_ci */
6962306a36Sopenharmony_civoid vmw_resource_mob_detach(struct vmw_resource *res)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	struct vmw_bo *gbo = res->guest_memory_bo;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	dma_resv_assert_held(gbo->tbo.base.resv);
7462306a36Sopenharmony_ci	if (vmw_resource_mob_attached(res)) {
7562306a36Sopenharmony_ci		rb_erase(&res->mob_node, &gbo->res_tree);
7662306a36Sopenharmony_ci		RB_CLEAR_NODE(&res->mob_node);
7762306a36Sopenharmony_ci		vmw_bo_prio_del(gbo, res->used_prio);
7862306a36Sopenharmony_ci	}
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cistruct vmw_resource *vmw_resource_reference(struct vmw_resource *res)
8262306a36Sopenharmony_ci{
8362306a36Sopenharmony_ci	kref_get(&res->kref);
8462306a36Sopenharmony_ci	return res;
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistruct vmw_resource *
8862306a36Sopenharmony_civmw_resource_reference_unless_doomed(struct vmw_resource *res)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	return kref_get_unless_zero(&res->kref) ? res : NULL;
9162306a36Sopenharmony_ci}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci/**
9462306a36Sopenharmony_ci * vmw_resource_release_id - release a resource id to the id manager.
9562306a36Sopenharmony_ci *
9662306a36Sopenharmony_ci * @res: Pointer to the resource.
9762306a36Sopenharmony_ci *
9862306a36Sopenharmony_ci * Release the resource id to the resource id manager and set it to -1
9962306a36Sopenharmony_ci */
10062306a36Sopenharmony_civoid vmw_resource_release_id(struct vmw_resource *res)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	struct vmw_private *dev_priv = res->dev_priv;
10362306a36Sopenharmony_ci	struct idr *idr = &dev_priv->res_idr[res->func->res_type];
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	spin_lock(&dev_priv->resource_lock);
10662306a36Sopenharmony_ci	if (res->id != -1)
10762306a36Sopenharmony_ci		idr_remove(idr, res->id);
10862306a36Sopenharmony_ci	res->id = -1;
10962306a36Sopenharmony_ci	spin_unlock(&dev_priv->resource_lock);
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cistatic void vmw_resource_release(struct kref *kref)
11362306a36Sopenharmony_ci{
11462306a36Sopenharmony_ci	struct vmw_resource *res =
11562306a36Sopenharmony_ci	    container_of(kref, struct vmw_resource, kref);
11662306a36Sopenharmony_ci	struct vmw_private *dev_priv = res->dev_priv;
11762306a36Sopenharmony_ci	int id;
11862306a36Sopenharmony_ci	int ret;
11962306a36Sopenharmony_ci	struct idr *idr = &dev_priv->res_idr[res->func->res_type];
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	spin_lock(&dev_priv->resource_lock);
12262306a36Sopenharmony_ci	list_del_init(&res->lru_head);
12362306a36Sopenharmony_ci	spin_unlock(&dev_priv->resource_lock);
12462306a36Sopenharmony_ci	if (res->guest_memory_bo) {
12562306a36Sopenharmony_ci		struct ttm_buffer_object *bo = &res->guest_memory_bo->tbo;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci		ret = ttm_bo_reserve(bo, false, false, NULL);
12862306a36Sopenharmony_ci		BUG_ON(ret);
12962306a36Sopenharmony_ci		if (vmw_resource_mob_attached(res) &&
13062306a36Sopenharmony_ci		    res->func->unbind != NULL) {
13162306a36Sopenharmony_ci			struct ttm_validate_buffer val_buf;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci			val_buf.bo = bo;
13462306a36Sopenharmony_ci			val_buf.num_shared = 0;
13562306a36Sopenharmony_ci			res->func->unbind(res, false, &val_buf);
13662306a36Sopenharmony_ci		}
13762306a36Sopenharmony_ci		res->guest_memory_size = false;
13862306a36Sopenharmony_ci		vmw_resource_mob_detach(res);
13962306a36Sopenharmony_ci		if (res->dirty)
14062306a36Sopenharmony_ci			res->func->dirty_free(res);
14162306a36Sopenharmony_ci		if (res->coherent)
14262306a36Sopenharmony_ci			vmw_bo_dirty_release(res->guest_memory_bo);
14362306a36Sopenharmony_ci		ttm_bo_unreserve(bo);
14462306a36Sopenharmony_ci		vmw_user_bo_unref(&res->guest_memory_bo);
14562306a36Sopenharmony_ci	}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	if (likely(res->hw_destroy != NULL)) {
14862306a36Sopenharmony_ci		mutex_lock(&dev_priv->binding_mutex);
14962306a36Sopenharmony_ci		vmw_binding_res_list_kill(&res->binding_head);
15062306a36Sopenharmony_ci		mutex_unlock(&dev_priv->binding_mutex);
15162306a36Sopenharmony_ci		res->hw_destroy(res);
15262306a36Sopenharmony_ci	}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	id = res->id;
15562306a36Sopenharmony_ci	if (res->res_free != NULL)
15662306a36Sopenharmony_ci		res->res_free(res);
15762306a36Sopenharmony_ci	else
15862306a36Sopenharmony_ci		kfree(res);
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	spin_lock(&dev_priv->resource_lock);
16162306a36Sopenharmony_ci	if (id != -1)
16262306a36Sopenharmony_ci		idr_remove(idr, id);
16362306a36Sopenharmony_ci	spin_unlock(&dev_priv->resource_lock);
16462306a36Sopenharmony_ci}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_civoid vmw_resource_unreference(struct vmw_resource **p_res)
16762306a36Sopenharmony_ci{
16862306a36Sopenharmony_ci	struct vmw_resource *res = *p_res;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	*p_res = NULL;
17162306a36Sopenharmony_ci	kref_put(&res->kref, vmw_resource_release);
17262306a36Sopenharmony_ci}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci/**
17662306a36Sopenharmony_ci * vmw_resource_alloc_id - release a resource id to the id manager.
17762306a36Sopenharmony_ci *
17862306a36Sopenharmony_ci * @res: Pointer to the resource.
17962306a36Sopenharmony_ci *
18062306a36Sopenharmony_ci * Allocate the lowest free resource from the resource manager, and set
18162306a36Sopenharmony_ci * @res->id to that id. Returns 0 on success and -ENOMEM on failure.
18262306a36Sopenharmony_ci */
18362306a36Sopenharmony_ciint vmw_resource_alloc_id(struct vmw_resource *res)
18462306a36Sopenharmony_ci{
18562306a36Sopenharmony_ci	struct vmw_private *dev_priv = res->dev_priv;
18662306a36Sopenharmony_ci	int ret;
18762306a36Sopenharmony_ci	struct idr *idr = &dev_priv->res_idr[res->func->res_type];
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	BUG_ON(res->id != -1);
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	idr_preload(GFP_KERNEL);
19262306a36Sopenharmony_ci	spin_lock(&dev_priv->resource_lock);
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	ret = idr_alloc(idr, res, 1, 0, GFP_NOWAIT);
19562306a36Sopenharmony_ci	if (ret >= 0)
19662306a36Sopenharmony_ci		res->id = ret;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	spin_unlock(&dev_priv->resource_lock);
19962306a36Sopenharmony_ci	idr_preload_end();
20062306a36Sopenharmony_ci	return ret < 0 ? ret : 0;
20162306a36Sopenharmony_ci}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci/**
20462306a36Sopenharmony_ci * vmw_resource_init - initialize a struct vmw_resource
20562306a36Sopenharmony_ci *
20662306a36Sopenharmony_ci * @dev_priv:       Pointer to a device private struct.
20762306a36Sopenharmony_ci * @res:            The struct vmw_resource to initialize.
20862306a36Sopenharmony_ci * @delay_id:       Boolean whether to defer device id allocation until
20962306a36Sopenharmony_ci *                  the first validation.
21062306a36Sopenharmony_ci * @res_free:       Resource destructor.
21162306a36Sopenharmony_ci * @func:           Resource function table.
21262306a36Sopenharmony_ci */
21362306a36Sopenharmony_ciint vmw_resource_init(struct vmw_private *dev_priv, struct vmw_resource *res,
21462306a36Sopenharmony_ci		      bool delay_id,
21562306a36Sopenharmony_ci		      void (*res_free) (struct vmw_resource *res),
21662306a36Sopenharmony_ci		      const struct vmw_res_func *func)
21762306a36Sopenharmony_ci{
21862306a36Sopenharmony_ci	kref_init(&res->kref);
21962306a36Sopenharmony_ci	res->hw_destroy = NULL;
22062306a36Sopenharmony_ci	res->res_free = res_free;
22162306a36Sopenharmony_ci	res->dev_priv = dev_priv;
22262306a36Sopenharmony_ci	res->func = func;
22362306a36Sopenharmony_ci	RB_CLEAR_NODE(&res->mob_node);
22462306a36Sopenharmony_ci	INIT_LIST_HEAD(&res->lru_head);
22562306a36Sopenharmony_ci	INIT_LIST_HEAD(&res->binding_head);
22662306a36Sopenharmony_ci	res->id = -1;
22762306a36Sopenharmony_ci	res->guest_memory_bo = NULL;
22862306a36Sopenharmony_ci	res->guest_memory_offset = 0;
22962306a36Sopenharmony_ci	res->guest_memory_dirty = false;
23062306a36Sopenharmony_ci	res->res_dirty = false;
23162306a36Sopenharmony_ci	res->coherent = false;
23262306a36Sopenharmony_ci	res->used_prio = 3;
23362306a36Sopenharmony_ci	res->dirty = NULL;
23462306a36Sopenharmony_ci	if (delay_id)
23562306a36Sopenharmony_ci		return 0;
23662306a36Sopenharmony_ci	else
23762306a36Sopenharmony_ci		return vmw_resource_alloc_id(res);
23862306a36Sopenharmony_ci}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci/**
24262306a36Sopenharmony_ci * vmw_user_resource_lookup_handle - lookup a struct resource from a
24362306a36Sopenharmony_ci * TTM user-space handle and perform basic type checks
24462306a36Sopenharmony_ci *
24562306a36Sopenharmony_ci * @dev_priv:     Pointer to a device private struct
24662306a36Sopenharmony_ci * @tfile:        Pointer to a struct ttm_object_file identifying the caller
24762306a36Sopenharmony_ci * @handle:       The TTM user-space handle
24862306a36Sopenharmony_ci * @converter:    Pointer to an object describing the resource type
24962306a36Sopenharmony_ci * @p_res:        On successful return the location pointed to will contain
25062306a36Sopenharmony_ci *                a pointer to a refcounted struct vmw_resource.
25162306a36Sopenharmony_ci *
25262306a36Sopenharmony_ci * If the handle can't be found or is associated with an incorrect resource
25362306a36Sopenharmony_ci * type, -EINVAL will be returned.
25462306a36Sopenharmony_ci */
25562306a36Sopenharmony_ciint vmw_user_resource_lookup_handle(struct vmw_private *dev_priv,
25662306a36Sopenharmony_ci				    struct ttm_object_file *tfile,
25762306a36Sopenharmony_ci				    uint32_t handle,
25862306a36Sopenharmony_ci				    const struct vmw_user_resource_conv
25962306a36Sopenharmony_ci				    *converter,
26062306a36Sopenharmony_ci				    struct vmw_resource **p_res)
26162306a36Sopenharmony_ci{
26262306a36Sopenharmony_ci	struct ttm_base_object *base;
26362306a36Sopenharmony_ci	struct vmw_resource *res;
26462306a36Sopenharmony_ci	int ret = -EINVAL;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	base = ttm_base_object_lookup(tfile, handle);
26762306a36Sopenharmony_ci	if (unlikely(!base))
26862306a36Sopenharmony_ci		return -EINVAL;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	if (unlikely(ttm_base_object_type(base) != converter->object_type))
27162306a36Sopenharmony_ci		goto out_bad_resource;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	res = converter->base_obj_to_res(base);
27462306a36Sopenharmony_ci	kref_get(&res->kref);
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	*p_res = res;
27762306a36Sopenharmony_ci	ret = 0;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ciout_bad_resource:
28062306a36Sopenharmony_ci	ttm_base_object_unref(&base);
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	return ret;
28362306a36Sopenharmony_ci}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci/*
28662306a36Sopenharmony_ci * Helper function that looks either a surface or bo.
28762306a36Sopenharmony_ci *
28862306a36Sopenharmony_ci * The pointer this pointed at by out_surf and out_buf needs to be null.
28962306a36Sopenharmony_ci */
29062306a36Sopenharmony_ciint vmw_user_lookup_handle(struct vmw_private *dev_priv,
29162306a36Sopenharmony_ci			   struct drm_file *filp,
29262306a36Sopenharmony_ci			   uint32_t handle,
29362306a36Sopenharmony_ci			   struct vmw_surface **out_surf,
29462306a36Sopenharmony_ci			   struct vmw_bo **out_buf)
29562306a36Sopenharmony_ci{
29662306a36Sopenharmony_ci	struct ttm_object_file *tfile = vmw_fpriv(filp)->tfile;
29762306a36Sopenharmony_ci	struct vmw_resource *res;
29862306a36Sopenharmony_ci	int ret;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	BUG_ON(*out_surf || *out_buf);
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	ret = vmw_user_resource_lookup_handle(dev_priv, tfile, handle,
30362306a36Sopenharmony_ci					      user_surface_converter,
30462306a36Sopenharmony_ci					      &res);
30562306a36Sopenharmony_ci	if (!ret) {
30662306a36Sopenharmony_ci		*out_surf = vmw_res_to_srf(res);
30762306a36Sopenharmony_ci		return 0;
30862306a36Sopenharmony_ci	}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	*out_surf = NULL;
31162306a36Sopenharmony_ci	ret = vmw_user_bo_lookup(filp, handle, out_buf);
31262306a36Sopenharmony_ci	return ret;
31362306a36Sopenharmony_ci}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci/**
31662306a36Sopenharmony_ci * vmw_resource_buf_alloc - Allocate a guest memory buffer for a resource.
31762306a36Sopenharmony_ci *
31862306a36Sopenharmony_ci * @res:            The resource for which to allocate a gbo buffer.
31962306a36Sopenharmony_ci * @interruptible:  Whether any sleeps during allocation should be
32062306a36Sopenharmony_ci *                  performed while interruptible.
32162306a36Sopenharmony_ci */
32262306a36Sopenharmony_cistatic int vmw_resource_buf_alloc(struct vmw_resource *res,
32362306a36Sopenharmony_ci				  bool interruptible)
32462306a36Sopenharmony_ci{
32562306a36Sopenharmony_ci	unsigned long size = PFN_ALIGN(res->guest_memory_size);
32662306a36Sopenharmony_ci	struct vmw_bo *gbo;
32762306a36Sopenharmony_ci	struct vmw_bo_params bo_params = {
32862306a36Sopenharmony_ci		.domain = res->func->domain,
32962306a36Sopenharmony_ci		.busy_domain = res->func->busy_domain,
33062306a36Sopenharmony_ci		.bo_type = ttm_bo_type_device,
33162306a36Sopenharmony_ci		.size = res->guest_memory_size,
33262306a36Sopenharmony_ci		.pin = false
33362306a36Sopenharmony_ci	};
33462306a36Sopenharmony_ci	int ret;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	if (likely(res->guest_memory_bo)) {
33762306a36Sopenharmony_ci		BUG_ON(res->guest_memory_bo->tbo.base.size < size);
33862306a36Sopenharmony_ci		return 0;
33962306a36Sopenharmony_ci	}
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	ret = vmw_gem_object_create(res->dev_priv, &bo_params, &gbo);
34262306a36Sopenharmony_ci	if (unlikely(ret != 0))
34362306a36Sopenharmony_ci		goto out_no_bo;
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	res->guest_memory_bo = gbo;
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ciout_no_bo:
34862306a36Sopenharmony_ci	return ret;
34962306a36Sopenharmony_ci}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci/**
35262306a36Sopenharmony_ci * vmw_resource_do_validate - Make a resource up-to-date and visible
35362306a36Sopenharmony_ci *                            to the device.
35462306a36Sopenharmony_ci *
35562306a36Sopenharmony_ci * @res:            The resource to make visible to the device.
35662306a36Sopenharmony_ci * @val_buf:        Information about a buffer possibly
35762306a36Sopenharmony_ci *                  containing backup data if a bind operation is needed.
35862306a36Sopenharmony_ci * @dirtying:       Transfer dirty regions.
35962306a36Sopenharmony_ci *
36062306a36Sopenharmony_ci * On hardware resource shortage, this function returns -EBUSY and
36162306a36Sopenharmony_ci * should be retried once resources have been freed up.
36262306a36Sopenharmony_ci */
36362306a36Sopenharmony_cistatic int vmw_resource_do_validate(struct vmw_resource *res,
36462306a36Sopenharmony_ci				    struct ttm_validate_buffer *val_buf,
36562306a36Sopenharmony_ci				    bool dirtying)
36662306a36Sopenharmony_ci{
36762306a36Sopenharmony_ci	int ret = 0;
36862306a36Sopenharmony_ci	const struct vmw_res_func *func = res->func;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	if (unlikely(res->id == -1)) {
37162306a36Sopenharmony_ci		ret = func->create(res);
37262306a36Sopenharmony_ci		if (unlikely(ret != 0))
37362306a36Sopenharmony_ci			return ret;
37462306a36Sopenharmony_ci	}
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	if (func->bind &&
37762306a36Sopenharmony_ci	    ((func->needs_guest_memory && !vmw_resource_mob_attached(res) &&
37862306a36Sopenharmony_ci	      val_buf->bo) ||
37962306a36Sopenharmony_ci	     (!func->needs_guest_memory && val_buf->bo))) {
38062306a36Sopenharmony_ci		ret = func->bind(res, val_buf);
38162306a36Sopenharmony_ci		if (unlikely(ret != 0))
38262306a36Sopenharmony_ci			goto out_bind_failed;
38362306a36Sopenharmony_ci		if (func->needs_guest_memory)
38462306a36Sopenharmony_ci			vmw_resource_mob_attach(res);
38562306a36Sopenharmony_ci	}
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	/*
38862306a36Sopenharmony_ci	 * Handle the case where the backup mob is marked coherent but
38962306a36Sopenharmony_ci	 * the resource isn't.
39062306a36Sopenharmony_ci	 */
39162306a36Sopenharmony_ci	if (func->dirty_alloc && vmw_resource_mob_attached(res) &&
39262306a36Sopenharmony_ci	    !res->coherent) {
39362306a36Sopenharmony_ci		if (res->guest_memory_bo->dirty && !res->dirty) {
39462306a36Sopenharmony_ci			ret = func->dirty_alloc(res);
39562306a36Sopenharmony_ci			if (ret)
39662306a36Sopenharmony_ci				return ret;
39762306a36Sopenharmony_ci		} else if (!res->guest_memory_bo->dirty && res->dirty) {
39862306a36Sopenharmony_ci			func->dirty_free(res);
39962306a36Sopenharmony_ci		}
40062306a36Sopenharmony_ci	}
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	/*
40362306a36Sopenharmony_ci	 * Transfer the dirty regions to the resource and update
40462306a36Sopenharmony_ci	 * the resource.
40562306a36Sopenharmony_ci	 */
40662306a36Sopenharmony_ci	if (res->dirty) {
40762306a36Sopenharmony_ci		if (dirtying && !res->res_dirty) {
40862306a36Sopenharmony_ci			pgoff_t start = res->guest_memory_offset >> PAGE_SHIFT;
40962306a36Sopenharmony_ci			pgoff_t end = __KERNEL_DIV_ROUND_UP
41062306a36Sopenharmony_ci				(res->guest_memory_offset + res->guest_memory_size,
41162306a36Sopenharmony_ci				 PAGE_SIZE);
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci			vmw_bo_dirty_unmap(res->guest_memory_bo, start, end);
41462306a36Sopenharmony_ci		}
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci		vmw_bo_dirty_transfer_to_res(res);
41762306a36Sopenharmony_ci		return func->dirty_sync(res);
41862306a36Sopenharmony_ci	}
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	return 0;
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ciout_bind_failed:
42362306a36Sopenharmony_ci	func->destroy(res);
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	return ret;
42662306a36Sopenharmony_ci}
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci/**
42962306a36Sopenharmony_ci * vmw_resource_unreserve - Unreserve a resource previously reserved for
43062306a36Sopenharmony_ci * command submission.
43162306a36Sopenharmony_ci *
43262306a36Sopenharmony_ci * @res:               Pointer to the struct vmw_resource to unreserve.
43362306a36Sopenharmony_ci * @dirty_set:         Change dirty status of the resource.
43462306a36Sopenharmony_ci * @dirty:             When changing dirty status indicates the new status.
43562306a36Sopenharmony_ci * @switch_guest_memory: Guest memory buffer has been switched.
43662306a36Sopenharmony_ci * @new_guest_memory_bo: Pointer to new guest memory buffer if command submission
43762306a36Sopenharmony_ci *                     switched. May be NULL.
43862306a36Sopenharmony_ci * @new_guest_memory_offset: New gbo offset if @switch_guest_memory is true.
43962306a36Sopenharmony_ci *
44062306a36Sopenharmony_ci * Currently unreserving a resource means putting it back on the device's
44162306a36Sopenharmony_ci * resource lru list, so that it can be evicted if necessary.
44262306a36Sopenharmony_ci */
44362306a36Sopenharmony_civoid vmw_resource_unreserve(struct vmw_resource *res,
44462306a36Sopenharmony_ci			    bool dirty_set,
44562306a36Sopenharmony_ci			    bool dirty,
44662306a36Sopenharmony_ci			    bool switch_guest_memory,
44762306a36Sopenharmony_ci			    struct vmw_bo *new_guest_memory_bo,
44862306a36Sopenharmony_ci			    unsigned long new_guest_memory_offset)
44962306a36Sopenharmony_ci{
45062306a36Sopenharmony_ci	struct vmw_private *dev_priv = res->dev_priv;
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	if (!list_empty(&res->lru_head))
45362306a36Sopenharmony_ci		return;
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	if (switch_guest_memory && new_guest_memory_bo != res->guest_memory_bo) {
45662306a36Sopenharmony_ci		if (res->guest_memory_bo) {
45762306a36Sopenharmony_ci			vmw_resource_mob_detach(res);
45862306a36Sopenharmony_ci			if (res->coherent)
45962306a36Sopenharmony_ci				vmw_bo_dirty_release(res->guest_memory_bo);
46062306a36Sopenharmony_ci			vmw_user_bo_unref(&res->guest_memory_bo);
46162306a36Sopenharmony_ci		}
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci		if (new_guest_memory_bo) {
46462306a36Sopenharmony_ci			res->guest_memory_bo = vmw_user_bo_ref(new_guest_memory_bo);
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci			/*
46762306a36Sopenharmony_ci			 * The validation code should already have added a
46862306a36Sopenharmony_ci			 * dirty tracker here.
46962306a36Sopenharmony_ci			 */
47062306a36Sopenharmony_ci			WARN_ON(res->coherent && !new_guest_memory_bo->dirty);
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci			vmw_resource_mob_attach(res);
47362306a36Sopenharmony_ci		} else {
47462306a36Sopenharmony_ci			res->guest_memory_bo = NULL;
47562306a36Sopenharmony_ci		}
47662306a36Sopenharmony_ci	} else if (switch_guest_memory && res->coherent) {
47762306a36Sopenharmony_ci		vmw_bo_dirty_release(res->guest_memory_bo);
47862306a36Sopenharmony_ci	}
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	if (switch_guest_memory)
48162306a36Sopenharmony_ci		res->guest_memory_offset = new_guest_memory_offset;
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	if (dirty_set)
48462306a36Sopenharmony_ci		res->res_dirty = dirty;
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	if (!res->func->may_evict || res->id == -1 || res->pin_count)
48762306a36Sopenharmony_ci		return;
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	spin_lock(&dev_priv->resource_lock);
49062306a36Sopenharmony_ci	list_add_tail(&res->lru_head,
49162306a36Sopenharmony_ci		      &res->dev_priv->res_lru[res->func->res_type]);
49262306a36Sopenharmony_ci	spin_unlock(&dev_priv->resource_lock);
49362306a36Sopenharmony_ci}
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci/**
49662306a36Sopenharmony_ci * vmw_resource_check_buffer - Check whether a backup buffer is needed
49762306a36Sopenharmony_ci *                             for a resource and in that case, allocate
49862306a36Sopenharmony_ci *                             one, reserve and validate it.
49962306a36Sopenharmony_ci *
50062306a36Sopenharmony_ci * @ticket:         The ww acquire context to use, or NULL if trylocking.
50162306a36Sopenharmony_ci * @res:            The resource for which to allocate a backup buffer.
50262306a36Sopenharmony_ci * @interruptible:  Whether any sleeps during allocation should be
50362306a36Sopenharmony_ci *                  performed while interruptible.
50462306a36Sopenharmony_ci * @val_buf:        On successful return contains data about the
50562306a36Sopenharmony_ci *                  reserved and validated backup buffer.
50662306a36Sopenharmony_ci */
50762306a36Sopenharmony_cistatic int
50862306a36Sopenharmony_civmw_resource_check_buffer(struct ww_acquire_ctx *ticket,
50962306a36Sopenharmony_ci			  struct vmw_resource *res,
51062306a36Sopenharmony_ci			  bool interruptible,
51162306a36Sopenharmony_ci			  struct ttm_validate_buffer *val_buf)
51262306a36Sopenharmony_ci{
51362306a36Sopenharmony_ci	struct ttm_operation_ctx ctx = { true, false };
51462306a36Sopenharmony_ci	struct list_head val_list;
51562306a36Sopenharmony_ci	bool guest_memory_dirty = false;
51662306a36Sopenharmony_ci	int ret;
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	if (unlikely(!res->guest_memory_bo)) {
51962306a36Sopenharmony_ci		ret = vmw_resource_buf_alloc(res, interruptible);
52062306a36Sopenharmony_ci		if (unlikely(ret != 0))
52162306a36Sopenharmony_ci			return ret;
52262306a36Sopenharmony_ci	}
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	INIT_LIST_HEAD(&val_list);
52562306a36Sopenharmony_ci	ttm_bo_get(&res->guest_memory_bo->tbo);
52662306a36Sopenharmony_ci	val_buf->bo = &res->guest_memory_bo->tbo;
52762306a36Sopenharmony_ci	val_buf->num_shared = 0;
52862306a36Sopenharmony_ci	list_add_tail(&val_buf->head, &val_list);
52962306a36Sopenharmony_ci	ret = ttm_eu_reserve_buffers(ticket, &val_list, interruptible, NULL);
53062306a36Sopenharmony_ci	if (unlikely(ret != 0))
53162306a36Sopenharmony_ci		goto out_no_reserve;
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	if (res->func->needs_guest_memory && !vmw_resource_mob_attached(res))
53462306a36Sopenharmony_ci		return 0;
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	guest_memory_dirty = res->guest_memory_dirty;
53762306a36Sopenharmony_ci	vmw_bo_placement_set(res->guest_memory_bo, res->func->domain,
53862306a36Sopenharmony_ci			     res->func->busy_domain);
53962306a36Sopenharmony_ci	ret = ttm_bo_validate(&res->guest_memory_bo->tbo,
54062306a36Sopenharmony_ci			      &res->guest_memory_bo->placement,
54162306a36Sopenharmony_ci			      &ctx);
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	if (unlikely(ret != 0))
54462306a36Sopenharmony_ci		goto out_no_validate;
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	return 0;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ciout_no_validate:
54962306a36Sopenharmony_ci	ttm_eu_backoff_reservation(ticket, &val_list);
55062306a36Sopenharmony_ciout_no_reserve:
55162306a36Sopenharmony_ci	ttm_bo_put(val_buf->bo);
55262306a36Sopenharmony_ci	val_buf->bo = NULL;
55362306a36Sopenharmony_ci	if (guest_memory_dirty)
55462306a36Sopenharmony_ci		vmw_user_bo_unref(&res->guest_memory_bo);
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	return ret;
55762306a36Sopenharmony_ci}
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci/*
56062306a36Sopenharmony_ci * vmw_resource_reserve - Reserve a resource for command submission
56162306a36Sopenharmony_ci *
56262306a36Sopenharmony_ci * @res:            The resource to reserve.
56362306a36Sopenharmony_ci *
56462306a36Sopenharmony_ci * This function takes the resource off the LRU list and make sure
56562306a36Sopenharmony_ci * a guest memory buffer is present for guest-backed resources.
56662306a36Sopenharmony_ci * However, the buffer may not be bound to the resource at this
56762306a36Sopenharmony_ci * point.
56862306a36Sopenharmony_ci *
56962306a36Sopenharmony_ci */
57062306a36Sopenharmony_ciint vmw_resource_reserve(struct vmw_resource *res, bool interruptible,
57162306a36Sopenharmony_ci			 bool no_guest_memory)
57262306a36Sopenharmony_ci{
57362306a36Sopenharmony_ci	struct vmw_private *dev_priv = res->dev_priv;
57462306a36Sopenharmony_ci	int ret;
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	spin_lock(&dev_priv->resource_lock);
57762306a36Sopenharmony_ci	list_del_init(&res->lru_head);
57862306a36Sopenharmony_ci	spin_unlock(&dev_priv->resource_lock);
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	if (res->func->needs_guest_memory && !res->guest_memory_bo &&
58162306a36Sopenharmony_ci	    !no_guest_memory) {
58262306a36Sopenharmony_ci		ret = vmw_resource_buf_alloc(res, interruptible);
58362306a36Sopenharmony_ci		if (unlikely(ret != 0)) {
58462306a36Sopenharmony_ci			DRM_ERROR("Failed to allocate a guest memory buffer "
58562306a36Sopenharmony_ci				  "of size %lu. bytes\n",
58662306a36Sopenharmony_ci				  (unsigned long) res->guest_memory_size);
58762306a36Sopenharmony_ci			return ret;
58862306a36Sopenharmony_ci		}
58962306a36Sopenharmony_ci	}
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	return 0;
59262306a36Sopenharmony_ci}
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci/**
59562306a36Sopenharmony_ci * vmw_resource_backoff_reservation - Unreserve and unreference a
59662306a36Sopenharmony_ci *                                    guest memory buffer
59762306a36Sopenharmony_ci *.
59862306a36Sopenharmony_ci * @ticket:         The ww acquire ctx used for reservation.
59962306a36Sopenharmony_ci * @val_buf:        Guest memory buffer information.
60062306a36Sopenharmony_ci */
60162306a36Sopenharmony_cistatic void
60262306a36Sopenharmony_civmw_resource_backoff_reservation(struct ww_acquire_ctx *ticket,
60362306a36Sopenharmony_ci				 struct ttm_validate_buffer *val_buf)
60462306a36Sopenharmony_ci{
60562306a36Sopenharmony_ci	struct list_head val_list;
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	if (likely(val_buf->bo == NULL))
60862306a36Sopenharmony_ci		return;
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	INIT_LIST_HEAD(&val_list);
61162306a36Sopenharmony_ci	list_add_tail(&val_buf->head, &val_list);
61262306a36Sopenharmony_ci	ttm_eu_backoff_reservation(ticket, &val_list);
61362306a36Sopenharmony_ci	ttm_bo_put(val_buf->bo);
61462306a36Sopenharmony_ci	val_buf->bo = NULL;
61562306a36Sopenharmony_ci}
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci/**
61862306a36Sopenharmony_ci * vmw_resource_do_evict - Evict a resource, and transfer its data
61962306a36Sopenharmony_ci *                         to a backup buffer.
62062306a36Sopenharmony_ci *
62162306a36Sopenharmony_ci * @ticket:         The ww acquire ticket to use, or NULL if trylocking.
62262306a36Sopenharmony_ci * @res:            The resource to evict.
62362306a36Sopenharmony_ci * @interruptible:  Whether to wait interruptible.
62462306a36Sopenharmony_ci */
62562306a36Sopenharmony_cistatic int vmw_resource_do_evict(struct ww_acquire_ctx *ticket,
62662306a36Sopenharmony_ci				 struct vmw_resource *res, bool interruptible)
62762306a36Sopenharmony_ci{
62862306a36Sopenharmony_ci	struct ttm_validate_buffer val_buf;
62962306a36Sopenharmony_ci	const struct vmw_res_func *func = res->func;
63062306a36Sopenharmony_ci	int ret;
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	BUG_ON(!func->may_evict);
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	val_buf.bo = NULL;
63562306a36Sopenharmony_ci	val_buf.num_shared = 0;
63662306a36Sopenharmony_ci	ret = vmw_resource_check_buffer(ticket, res, interruptible, &val_buf);
63762306a36Sopenharmony_ci	if (unlikely(ret != 0))
63862306a36Sopenharmony_ci		return ret;
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	if (unlikely(func->unbind != NULL &&
64162306a36Sopenharmony_ci		     (!func->needs_guest_memory || vmw_resource_mob_attached(res)))) {
64262306a36Sopenharmony_ci		ret = func->unbind(res, res->res_dirty, &val_buf);
64362306a36Sopenharmony_ci		if (unlikely(ret != 0))
64462306a36Sopenharmony_ci			goto out_no_unbind;
64562306a36Sopenharmony_ci		vmw_resource_mob_detach(res);
64662306a36Sopenharmony_ci	}
64762306a36Sopenharmony_ci	ret = func->destroy(res);
64862306a36Sopenharmony_ci	res->guest_memory_dirty = true;
64962306a36Sopenharmony_ci	res->res_dirty = false;
65062306a36Sopenharmony_ciout_no_unbind:
65162306a36Sopenharmony_ci	vmw_resource_backoff_reservation(ticket, &val_buf);
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	return ret;
65462306a36Sopenharmony_ci}
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci/**
65862306a36Sopenharmony_ci * vmw_resource_validate - Make a resource up-to-date and visible
65962306a36Sopenharmony_ci *                         to the device.
66062306a36Sopenharmony_ci * @res: The resource to make visible to the device.
66162306a36Sopenharmony_ci * @intr: Perform waits interruptible if possible.
66262306a36Sopenharmony_ci * @dirtying: Pending GPU operation will dirty the resource
66362306a36Sopenharmony_ci *
66462306a36Sopenharmony_ci * On successful return, any backup DMA buffer pointed to by @res->backup will
66562306a36Sopenharmony_ci * be reserved and validated.
66662306a36Sopenharmony_ci * On hardware resource shortage, this function will repeatedly evict
66762306a36Sopenharmony_ci * resources of the same type until the validation succeeds.
66862306a36Sopenharmony_ci *
66962306a36Sopenharmony_ci * Return: Zero on success, -ERESTARTSYS if interrupted, negative error code
67062306a36Sopenharmony_ci * on failure.
67162306a36Sopenharmony_ci */
67262306a36Sopenharmony_ciint vmw_resource_validate(struct vmw_resource *res, bool intr,
67362306a36Sopenharmony_ci			  bool dirtying)
67462306a36Sopenharmony_ci{
67562306a36Sopenharmony_ci	int ret;
67662306a36Sopenharmony_ci	struct vmw_resource *evict_res;
67762306a36Sopenharmony_ci	struct vmw_private *dev_priv = res->dev_priv;
67862306a36Sopenharmony_ci	struct list_head *lru_list = &dev_priv->res_lru[res->func->res_type];
67962306a36Sopenharmony_ci	struct ttm_validate_buffer val_buf;
68062306a36Sopenharmony_ci	unsigned err_count = 0;
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	if (!res->func->create)
68362306a36Sopenharmony_ci		return 0;
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	val_buf.bo = NULL;
68662306a36Sopenharmony_ci	val_buf.num_shared = 0;
68762306a36Sopenharmony_ci	if (res->guest_memory_bo)
68862306a36Sopenharmony_ci		val_buf.bo = &res->guest_memory_bo->tbo;
68962306a36Sopenharmony_ci	do {
69062306a36Sopenharmony_ci		ret = vmw_resource_do_validate(res, &val_buf, dirtying);
69162306a36Sopenharmony_ci		if (likely(ret != -EBUSY))
69262306a36Sopenharmony_ci			break;
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci		spin_lock(&dev_priv->resource_lock);
69562306a36Sopenharmony_ci		if (list_empty(lru_list) || !res->func->may_evict) {
69662306a36Sopenharmony_ci			DRM_ERROR("Out of device device resources "
69762306a36Sopenharmony_ci				  "for %s.\n", res->func->type_name);
69862306a36Sopenharmony_ci			ret = -EBUSY;
69962306a36Sopenharmony_ci			spin_unlock(&dev_priv->resource_lock);
70062306a36Sopenharmony_ci			break;
70162306a36Sopenharmony_ci		}
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci		evict_res = vmw_resource_reference
70462306a36Sopenharmony_ci			(list_first_entry(lru_list, struct vmw_resource,
70562306a36Sopenharmony_ci					  lru_head));
70662306a36Sopenharmony_ci		list_del_init(&evict_res->lru_head);
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci		spin_unlock(&dev_priv->resource_lock);
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci		/* Trylock backup buffers with a NULL ticket. */
71162306a36Sopenharmony_ci		ret = vmw_resource_do_evict(NULL, evict_res, intr);
71262306a36Sopenharmony_ci		if (unlikely(ret != 0)) {
71362306a36Sopenharmony_ci			spin_lock(&dev_priv->resource_lock);
71462306a36Sopenharmony_ci			list_add_tail(&evict_res->lru_head, lru_list);
71562306a36Sopenharmony_ci			spin_unlock(&dev_priv->resource_lock);
71662306a36Sopenharmony_ci			if (ret == -ERESTARTSYS ||
71762306a36Sopenharmony_ci			    ++err_count > VMW_RES_EVICT_ERR_COUNT) {
71862306a36Sopenharmony_ci				vmw_resource_unreference(&evict_res);
71962306a36Sopenharmony_ci				goto out_no_validate;
72062306a36Sopenharmony_ci			}
72162306a36Sopenharmony_ci		}
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci		vmw_resource_unreference(&evict_res);
72462306a36Sopenharmony_ci	} while (1);
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	if (unlikely(ret != 0))
72762306a36Sopenharmony_ci		goto out_no_validate;
72862306a36Sopenharmony_ci	else if (!res->func->needs_guest_memory && res->guest_memory_bo) {
72962306a36Sopenharmony_ci		WARN_ON_ONCE(vmw_resource_mob_attached(res));
73062306a36Sopenharmony_ci		vmw_user_bo_unref(&res->guest_memory_bo);
73162306a36Sopenharmony_ci	}
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci	return 0;
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ciout_no_validate:
73662306a36Sopenharmony_ci	return ret;
73762306a36Sopenharmony_ci}
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci/**
74162306a36Sopenharmony_ci * vmw_resource_unbind_list
74262306a36Sopenharmony_ci *
74362306a36Sopenharmony_ci * @vbo: Pointer to the current backing MOB.
74462306a36Sopenharmony_ci *
74562306a36Sopenharmony_ci * Evicts the Guest Backed hardware resource if the backup
74662306a36Sopenharmony_ci * buffer is being moved out of MOB memory.
74762306a36Sopenharmony_ci * Note that this function will not race with the resource
74862306a36Sopenharmony_ci * validation code, since resource validation and eviction
74962306a36Sopenharmony_ci * both require the backup buffer to be reserved.
75062306a36Sopenharmony_ci */
75162306a36Sopenharmony_civoid vmw_resource_unbind_list(struct vmw_bo *vbo)
75262306a36Sopenharmony_ci{
75362306a36Sopenharmony_ci	struct ttm_validate_buffer val_buf = {
75462306a36Sopenharmony_ci		.bo = &vbo->tbo,
75562306a36Sopenharmony_ci		.num_shared = 0
75662306a36Sopenharmony_ci	};
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci	dma_resv_assert_held(vbo->tbo.base.resv);
75962306a36Sopenharmony_ci	while (!RB_EMPTY_ROOT(&vbo->res_tree)) {
76062306a36Sopenharmony_ci		struct rb_node *node = vbo->res_tree.rb_node;
76162306a36Sopenharmony_ci		struct vmw_resource *res =
76262306a36Sopenharmony_ci			container_of(node, struct vmw_resource, mob_node);
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci		if (!WARN_ON_ONCE(!res->func->unbind))
76562306a36Sopenharmony_ci			(void) res->func->unbind(res, res->res_dirty, &val_buf);
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci		res->guest_memory_size = true;
76862306a36Sopenharmony_ci		res->res_dirty = false;
76962306a36Sopenharmony_ci		vmw_resource_mob_detach(res);
77062306a36Sopenharmony_ci	}
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	(void) ttm_bo_wait(&vbo->tbo, false, false);
77362306a36Sopenharmony_ci}
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci/**
77762306a36Sopenharmony_ci * vmw_query_readback_all - Read back cached query states
77862306a36Sopenharmony_ci *
77962306a36Sopenharmony_ci * @dx_query_mob: Buffer containing the DX query MOB
78062306a36Sopenharmony_ci *
78162306a36Sopenharmony_ci * Read back cached states from the device if they exist.  This function
78262306a36Sopenharmony_ci * assumes binding_mutex is held.
78362306a36Sopenharmony_ci */
78462306a36Sopenharmony_ciint vmw_query_readback_all(struct vmw_bo *dx_query_mob)
78562306a36Sopenharmony_ci{
78662306a36Sopenharmony_ci	struct vmw_resource *dx_query_ctx;
78762306a36Sopenharmony_ci	struct vmw_private *dev_priv;
78862306a36Sopenharmony_ci	struct {
78962306a36Sopenharmony_ci		SVGA3dCmdHeader header;
79062306a36Sopenharmony_ci		SVGA3dCmdDXReadbackAllQuery body;
79162306a36Sopenharmony_ci	} *cmd;
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	/* No query bound, so do nothing */
79562306a36Sopenharmony_ci	if (!dx_query_mob || !dx_query_mob->dx_query_ctx)
79662306a36Sopenharmony_ci		return 0;
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci	dx_query_ctx = dx_query_mob->dx_query_ctx;
79962306a36Sopenharmony_ci	dev_priv     = dx_query_ctx->dev_priv;
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci	cmd = VMW_CMD_CTX_RESERVE(dev_priv, sizeof(*cmd), dx_query_ctx->id);
80262306a36Sopenharmony_ci	if (unlikely(cmd == NULL))
80362306a36Sopenharmony_ci		return -ENOMEM;
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci	cmd->header.id   = SVGA_3D_CMD_DX_READBACK_ALL_QUERY;
80662306a36Sopenharmony_ci	cmd->header.size = sizeof(cmd->body);
80762306a36Sopenharmony_ci	cmd->body.cid    = dx_query_ctx->id;
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	vmw_cmd_commit(dev_priv, sizeof(*cmd));
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	/* Triggers a rebind the next time affected context is bound */
81262306a36Sopenharmony_ci	dx_query_mob->dx_query_ctx = NULL;
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci	return 0;
81562306a36Sopenharmony_ci}
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci/**
82062306a36Sopenharmony_ci * vmw_query_move_notify - Read back cached query states
82162306a36Sopenharmony_ci *
82262306a36Sopenharmony_ci * @bo: The TTM buffer object about to move.
82362306a36Sopenharmony_ci * @old_mem: The memory region @bo is moving from.
82462306a36Sopenharmony_ci * @new_mem: The memory region @bo is moving to.
82562306a36Sopenharmony_ci *
82662306a36Sopenharmony_ci * Called before the query MOB is swapped out to read back cached query
82762306a36Sopenharmony_ci * states from the device.
82862306a36Sopenharmony_ci */
82962306a36Sopenharmony_civoid vmw_query_move_notify(struct ttm_buffer_object *bo,
83062306a36Sopenharmony_ci			   struct ttm_resource *old_mem,
83162306a36Sopenharmony_ci			   struct ttm_resource *new_mem)
83262306a36Sopenharmony_ci{
83362306a36Sopenharmony_ci	struct vmw_bo *dx_query_mob;
83462306a36Sopenharmony_ci	struct ttm_device *bdev = bo->bdev;
83562306a36Sopenharmony_ci	struct vmw_private *dev_priv = vmw_priv_from_ttm(bdev);
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci	mutex_lock(&dev_priv->binding_mutex);
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	/* If BO is being moved from MOB to system memory */
84062306a36Sopenharmony_ci	if (old_mem &&
84162306a36Sopenharmony_ci	    new_mem->mem_type == TTM_PL_SYSTEM &&
84262306a36Sopenharmony_ci	    old_mem->mem_type == VMW_PL_MOB) {
84362306a36Sopenharmony_ci		struct vmw_fence_obj *fence;
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci		dx_query_mob = to_vmw_bo(&bo->base);
84662306a36Sopenharmony_ci		if (!dx_query_mob || !dx_query_mob->dx_query_ctx) {
84762306a36Sopenharmony_ci			mutex_unlock(&dev_priv->binding_mutex);
84862306a36Sopenharmony_ci			return;
84962306a36Sopenharmony_ci		}
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci		(void) vmw_query_readback_all(dx_query_mob);
85262306a36Sopenharmony_ci		mutex_unlock(&dev_priv->binding_mutex);
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci		/* Create a fence and attach the BO to it */
85562306a36Sopenharmony_ci		(void) vmw_execbuf_fence_commands(NULL, dev_priv, &fence, NULL);
85662306a36Sopenharmony_ci		vmw_bo_fence_single(bo, fence);
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci		if (fence != NULL)
85962306a36Sopenharmony_ci			vmw_fence_obj_unreference(&fence);
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci		(void) ttm_bo_wait(bo, false, false);
86262306a36Sopenharmony_ci	} else
86362306a36Sopenharmony_ci		mutex_unlock(&dev_priv->binding_mutex);
86462306a36Sopenharmony_ci}
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci/**
86762306a36Sopenharmony_ci * vmw_resource_needs_backup - Return whether a resource needs a backup buffer.
86862306a36Sopenharmony_ci *
86962306a36Sopenharmony_ci * @res:            The resource being queried.
87062306a36Sopenharmony_ci */
87162306a36Sopenharmony_cibool vmw_resource_needs_backup(const struct vmw_resource *res)
87262306a36Sopenharmony_ci{
87362306a36Sopenharmony_ci	return res->func->needs_guest_memory;
87462306a36Sopenharmony_ci}
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci/**
87762306a36Sopenharmony_ci * vmw_resource_evict_type - Evict all resources of a specific type
87862306a36Sopenharmony_ci *
87962306a36Sopenharmony_ci * @dev_priv:       Pointer to a device private struct
88062306a36Sopenharmony_ci * @type:           The resource type to evict
88162306a36Sopenharmony_ci *
88262306a36Sopenharmony_ci * To avoid thrashing starvation or as part of the hibernation sequence,
88362306a36Sopenharmony_ci * try to evict all evictable resources of a specific type.
88462306a36Sopenharmony_ci */
88562306a36Sopenharmony_cistatic void vmw_resource_evict_type(struct vmw_private *dev_priv,
88662306a36Sopenharmony_ci				    enum vmw_res_type type)
88762306a36Sopenharmony_ci{
88862306a36Sopenharmony_ci	struct list_head *lru_list = &dev_priv->res_lru[type];
88962306a36Sopenharmony_ci	struct vmw_resource *evict_res;
89062306a36Sopenharmony_ci	unsigned err_count = 0;
89162306a36Sopenharmony_ci	int ret;
89262306a36Sopenharmony_ci	struct ww_acquire_ctx ticket;
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci	do {
89562306a36Sopenharmony_ci		spin_lock(&dev_priv->resource_lock);
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci		if (list_empty(lru_list))
89862306a36Sopenharmony_ci			goto out_unlock;
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci		evict_res = vmw_resource_reference(
90162306a36Sopenharmony_ci			list_first_entry(lru_list, struct vmw_resource,
90262306a36Sopenharmony_ci					 lru_head));
90362306a36Sopenharmony_ci		list_del_init(&evict_res->lru_head);
90462306a36Sopenharmony_ci		spin_unlock(&dev_priv->resource_lock);
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci		/* Wait lock backup buffers with a ticket. */
90762306a36Sopenharmony_ci		ret = vmw_resource_do_evict(&ticket, evict_res, false);
90862306a36Sopenharmony_ci		if (unlikely(ret != 0)) {
90962306a36Sopenharmony_ci			spin_lock(&dev_priv->resource_lock);
91062306a36Sopenharmony_ci			list_add_tail(&evict_res->lru_head, lru_list);
91162306a36Sopenharmony_ci			spin_unlock(&dev_priv->resource_lock);
91262306a36Sopenharmony_ci			if (++err_count > VMW_RES_EVICT_ERR_COUNT) {
91362306a36Sopenharmony_ci				vmw_resource_unreference(&evict_res);
91462306a36Sopenharmony_ci				return;
91562306a36Sopenharmony_ci			}
91662306a36Sopenharmony_ci		}
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci		vmw_resource_unreference(&evict_res);
91962306a36Sopenharmony_ci	} while (1);
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ciout_unlock:
92262306a36Sopenharmony_ci	spin_unlock(&dev_priv->resource_lock);
92362306a36Sopenharmony_ci}
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci/**
92662306a36Sopenharmony_ci * vmw_resource_evict_all - Evict all evictable resources
92762306a36Sopenharmony_ci *
92862306a36Sopenharmony_ci * @dev_priv:       Pointer to a device private struct
92962306a36Sopenharmony_ci *
93062306a36Sopenharmony_ci * To avoid thrashing starvation or as part of the hibernation sequence,
93162306a36Sopenharmony_ci * evict all evictable resources. In particular this means that all
93262306a36Sopenharmony_ci * guest-backed resources that are registered with the device are
93362306a36Sopenharmony_ci * evicted and the OTable becomes clean.
93462306a36Sopenharmony_ci */
93562306a36Sopenharmony_civoid vmw_resource_evict_all(struct vmw_private *dev_priv)
93662306a36Sopenharmony_ci{
93762306a36Sopenharmony_ci	enum vmw_res_type type;
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci	mutex_lock(&dev_priv->cmdbuf_mutex);
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci	for (type = 0; type < vmw_res_max; ++type)
94262306a36Sopenharmony_ci		vmw_resource_evict_type(dev_priv, type);
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_ci	mutex_unlock(&dev_priv->cmdbuf_mutex);
94562306a36Sopenharmony_ci}
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_ci/*
94862306a36Sopenharmony_ci * vmw_resource_pin - Add a pin reference on a resource
94962306a36Sopenharmony_ci *
95062306a36Sopenharmony_ci * @res: The resource to add a pin reference on
95162306a36Sopenharmony_ci *
95262306a36Sopenharmony_ci * This function adds a pin reference, and if needed validates the resource.
95362306a36Sopenharmony_ci * Having a pin reference means that the resource can never be evicted, and
95462306a36Sopenharmony_ci * its id will never change as long as there is a pin reference.
95562306a36Sopenharmony_ci * This function returns 0 on success and a negative error code on failure.
95662306a36Sopenharmony_ci */
95762306a36Sopenharmony_ciint vmw_resource_pin(struct vmw_resource *res, bool interruptible)
95862306a36Sopenharmony_ci{
95962306a36Sopenharmony_ci	struct ttm_operation_ctx ctx = { interruptible, false };
96062306a36Sopenharmony_ci	struct vmw_private *dev_priv = res->dev_priv;
96162306a36Sopenharmony_ci	int ret;
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci	mutex_lock(&dev_priv->cmdbuf_mutex);
96462306a36Sopenharmony_ci	ret = vmw_resource_reserve(res, interruptible, false);
96562306a36Sopenharmony_ci	if (ret)
96662306a36Sopenharmony_ci		goto out_no_reserve;
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci	if (res->pin_count == 0) {
96962306a36Sopenharmony_ci		struct vmw_bo *vbo = NULL;
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci		if (res->guest_memory_bo) {
97262306a36Sopenharmony_ci			vbo = res->guest_memory_bo;
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_ci			ret = ttm_bo_reserve(&vbo->tbo, interruptible, false, NULL);
97562306a36Sopenharmony_ci			if (ret)
97662306a36Sopenharmony_ci				goto out_no_validate;
97762306a36Sopenharmony_ci			if (!vbo->tbo.pin_count) {
97862306a36Sopenharmony_ci				vmw_bo_placement_set(vbo,
97962306a36Sopenharmony_ci						     res->func->domain,
98062306a36Sopenharmony_ci						     res->func->busy_domain);
98162306a36Sopenharmony_ci				ret = ttm_bo_validate
98262306a36Sopenharmony_ci					(&vbo->tbo,
98362306a36Sopenharmony_ci					 &vbo->placement,
98462306a36Sopenharmony_ci					 &ctx);
98562306a36Sopenharmony_ci				if (ret) {
98662306a36Sopenharmony_ci					ttm_bo_unreserve(&vbo->tbo);
98762306a36Sopenharmony_ci					goto out_no_validate;
98862306a36Sopenharmony_ci				}
98962306a36Sopenharmony_ci			}
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci			/* Do we really need to pin the MOB as well? */
99262306a36Sopenharmony_ci			vmw_bo_pin_reserved(vbo, true);
99362306a36Sopenharmony_ci		}
99462306a36Sopenharmony_ci		ret = vmw_resource_validate(res, interruptible, true);
99562306a36Sopenharmony_ci		if (vbo)
99662306a36Sopenharmony_ci			ttm_bo_unreserve(&vbo->tbo);
99762306a36Sopenharmony_ci		if (ret)
99862306a36Sopenharmony_ci			goto out_no_validate;
99962306a36Sopenharmony_ci	}
100062306a36Sopenharmony_ci	res->pin_count++;
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ciout_no_validate:
100362306a36Sopenharmony_ci	vmw_resource_unreserve(res, false, false, false, NULL, 0UL);
100462306a36Sopenharmony_ciout_no_reserve:
100562306a36Sopenharmony_ci	mutex_unlock(&dev_priv->cmdbuf_mutex);
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci	return ret;
100862306a36Sopenharmony_ci}
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci/**
101162306a36Sopenharmony_ci * vmw_resource_unpin - Remove a pin reference from a resource
101262306a36Sopenharmony_ci *
101362306a36Sopenharmony_ci * @res: The resource to remove a pin reference from
101462306a36Sopenharmony_ci *
101562306a36Sopenharmony_ci * Having a pin reference means that the resource can never be evicted, and
101662306a36Sopenharmony_ci * its id will never change as long as there is a pin reference.
101762306a36Sopenharmony_ci */
101862306a36Sopenharmony_civoid vmw_resource_unpin(struct vmw_resource *res)
101962306a36Sopenharmony_ci{
102062306a36Sopenharmony_ci	struct vmw_private *dev_priv = res->dev_priv;
102162306a36Sopenharmony_ci	int ret;
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	mutex_lock(&dev_priv->cmdbuf_mutex);
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_ci	ret = vmw_resource_reserve(res, false, true);
102662306a36Sopenharmony_ci	WARN_ON(ret);
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci	WARN_ON(res->pin_count == 0);
102962306a36Sopenharmony_ci	if (--res->pin_count == 0 && res->guest_memory_bo) {
103062306a36Sopenharmony_ci		struct vmw_bo *vbo = res->guest_memory_bo;
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci		(void) ttm_bo_reserve(&vbo->tbo, false, false, NULL);
103362306a36Sopenharmony_ci		vmw_bo_pin_reserved(vbo, false);
103462306a36Sopenharmony_ci		ttm_bo_unreserve(&vbo->tbo);
103562306a36Sopenharmony_ci	}
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_ci	vmw_resource_unreserve(res, false, false, false, NULL, 0UL);
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_ci	mutex_unlock(&dev_priv->cmdbuf_mutex);
104062306a36Sopenharmony_ci}
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci/**
104362306a36Sopenharmony_ci * vmw_res_type - Return the resource type
104462306a36Sopenharmony_ci *
104562306a36Sopenharmony_ci * @res: Pointer to the resource
104662306a36Sopenharmony_ci */
104762306a36Sopenharmony_cienum vmw_res_type vmw_res_type(const struct vmw_resource *res)
104862306a36Sopenharmony_ci{
104962306a36Sopenharmony_ci	return res->func->res_type;
105062306a36Sopenharmony_ci}
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci/**
105362306a36Sopenharmony_ci * vmw_resource_dirty_update - Update a resource's dirty tracker with a
105462306a36Sopenharmony_ci * sequential range of touched backing store memory.
105562306a36Sopenharmony_ci * @res: The resource.
105662306a36Sopenharmony_ci * @start: The first page touched.
105762306a36Sopenharmony_ci * @end: The last page touched + 1.
105862306a36Sopenharmony_ci */
105962306a36Sopenharmony_civoid vmw_resource_dirty_update(struct vmw_resource *res, pgoff_t start,
106062306a36Sopenharmony_ci			       pgoff_t end)
106162306a36Sopenharmony_ci{
106262306a36Sopenharmony_ci	if (res->dirty)
106362306a36Sopenharmony_ci		res->func->dirty_range_add(res, start << PAGE_SHIFT,
106462306a36Sopenharmony_ci					   end << PAGE_SHIFT);
106562306a36Sopenharmony_ci}
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_ci/**
106862306a36Sopenharmony_ci * vmw_resources_clean - Clean resources intersecting a mob range
106962306a36Sopenharmony_ci * @vbo: The mob buffer object
107062306a36Sopenharmony_ci * @start: The mob page offset starting the range
107162306a36Sopenharmony_ci * @end: The mob page offset ending the range
107262306a36Sopenharmony_ci * @num_prefault: Returns how many pages including the first have been
107362306a36Sopenharmony_ci * cleaned and are ok to prefault
107462306a36Sopenharmony_ci */
107562306a36Sopenharmony_ciint vmw_resources_clean(struct vmw_bo *vbo, pgoff_t start,
107662306a36Sopenharmony_ci			pgoff_t end, pgoff_t *num_prefault)
107762306a36Sopenharmony_ci{
107862306a36Sopenharmony_ci	struct rb_node *cur = vbo->res_tree.rb_node;
107962306a36Sopenharmony_ci	struct vmw_resource *found = NULL;
108062306a36Sopenharmony_ci	unsigned long res_start = start << PAGE_SHIFT;
108162306a36Sopenharmony_ci	unsigned long res_end = end << PAGE_SHIFT;
108262306a36Sopenharmony_ci	unsigned long last_cleaned = 0;
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_ci	/*
108562306a36Sopenharmony_ci	 * Find the resource with lowest backup_offset that intersects the
108662306a36Sopenharmony_ci	 * range.
108762306a36Sopenharmony_ci	 */
108862306a36Sopenharmony_ci	while (cur) {
108962306a36Sopenharmony_ci		struct vmw_resource *cur_res =
109062306a36Sopenharmony_ci			container_of(cur, struct vmw_resource, mob_node);
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci		if (cur_res->guest_memory_offset >= res_end) {
109362306a36Sopenharmony_ci			cur = cur->rb_left;
109462306a36Sopenharmony_ci		} else if (cur_res->guest_memory_offset + cur_res->guest_memory_size <=
109562306a36Sopenharmony_ci			   res_start) {
109662306a36Sopenharmony_ci			cur = cur->rb_right;
109762306a36Sopenharmony_ci		} else {
109862306a36Sopenharmony_ci			found = cur_res;
109962306a36Sopenharmony_ci			cur = cur->rb_left;
110062306a36Sopenharmony_ci			/* Continue to look for resources with lower offsets */
110162306a36Sopenharmony_ci		}
110262306a36Sopenharmony_ci	}
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_ci	/*
110562306a36Sopenharmony_ci	 * In order of increasing guest_memory_offset, clean dirty resources
110662306a36Sopenharmony_ci	 * intersecting the range.
110762306a36Sopenharmony_ci	 */
110862306a36Sopenharmony_ci	while (found) {
110962306a36Sopenharmony_ci		if (found->res_dirty) {
111062306a36Sopenharmony_ci			int ret;
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_ci			if (!found->func->clean)
111362306a36Sopenharmony_ci				return -EINVAL;
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_ci			ret = found->func->clean(found);
111662306a36Sopenharmony_ci			if (ret)
111762306a36Sopenharmony_ci				return ret;
111862306a36Sopenharmony_ci
111962306a36Sopenharmony_ci			found->res_dirty = false;
112062306a36Sopenharmony_ci		}
112162306a36Sopenharmony_ci		last_cleaned = found->guest_memory_offset + found->guest_memory_size;
112262306a36Sopenharmony_ci		cur = rb_next(&found->mob_node);
112362306a36Sopenharmony_ci		if (!cur)
112462306a36Sopenharmony_ci			break;
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci		found = container_of(cur, struct vmw_resource, mob_node);
112762306a36Sopenharmony_ci		if (found->guest_memory_offset >= res_end)
112862306a36Sopenharmony_ci			break;
112962306a36Sopenharmony_ci	}
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_ci	/*
113262306a36Sopenharmony_ci	 * Set number of pages allowed prefaulting and fence the buffer object
113362306a36Sopenharmony_ci	 */
113462306a36Sopenharmony_ci	*num_prefault = 1;
113562306a36Sopenharmony_ci	if (last_cleaned > res_start) {
113662306a36Sopenharmony_ci		struct ttm_buffer_object *bo = &vbo->tbo;
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_ci		*num_prefault = __KERNEL_DIV_ROUND_UP(last_cleaned - res_start,
113962306a36Sopenharmony_ci						      PAGE_SIZE);
114062306a36Sopenharmony_ci		vmw_bo_fence_single(bo, NULL);
114162306a36Sopenharmony_ci	}
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_ci	return 0;
114462306a36Sopenharmony_ci}
1145