18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR MIT
28c2ecf20Sopenharmony_ci/**************************************************************************
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright 2011-2014 VMware, Inc., Palo Alto, CA., USA
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
78c2ecf20Sopenharmony_ci * copy of this software and associated documentation files (the
88c2ecf20Sopenharmony_ci * "Software"), to deal in the Software without restriction, including
98c2ecf20Sopenharmony_ci * without limitation the rights to use, copy, modify, merge, publish,
108c2ecf20Sopenharmony_ci * distribute, sub license, and/or sell copies of the Software, and to
118c2ecf20Sopenharmony_ci * permit persons to whom the Software is furnished to do so, subject to
128c2ecf20Sopenharmony_ci * the following conditions:
138c2ecf20Sopenharmony_ci *
148c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice (including the
158c2ecf20Sopenharmony_ci * next paragraph) shall be included in all copies or substantial portions
168c2ecf20Sopenharmony_ci * of the Software.
178c2ecf20Sopenharmony_ci *
188c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
198c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
208c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
218c2ecf20Sopenharmony_ci * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
228c2ecf20Sopenharmony_ci * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
238c2ecf20Sopenharmony_ci * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
248c2ecf20Sopenharmony_ci * USE OR OTHER DEALINGS IN THE SOFTWARE.
258c2ecf20Sopenharmony_ci *
268c2ecf20Sopenharmony_ci **************************************************************************/
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#include <linux/sched/signal.h>
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#include "vmwgfx_drv.h"
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#define VMW_FENCE_WRAP (1 << 31)
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_cistruct vmw_fence_manager {
358c2ecf20Sopenharmony_ci	int num_fence_objects;
368c2ecf20Sopenharmony_ci	struct vmw_private *dev_priv;
378c2ecf20Sopenharmony_ci	spinlock_t lock;
388c2ecf20Sopenharmony_ci	struct list_head fence_list;
398c2ecf20Sopenharmony_ci	struct work_struct work;
408c2ecf20Sopenharmony_ci	u32 user_fence_size;
418c2ecf20Sopenharmony_ci	u32 fence_size;
428c2ecf20Sopenharmony_ci	u32 event_fence_action_size;
438c2ecf20Sopenharmony_ci	bool fifo_down;
448c2ecf20Sopenharmony_ci	struct list_head cleanup_list;
458c2ecf20Sopenharmony_ci	uint32_t pending_actions[VMW_ACTION_MAX];
468c2ecf20Sopenharmony_ci	struct mutex goal_irq_mutex;
478c2ecf20Sopenharmony_ci	bool goal_irq_on; /* Protected by @goal_irq_mutex */
488c2ecf20Sopenharmony_ci	bool seqno_valid; /* Protected by @lock, and may not be set to true
498c2ecf20Sopenharmony_ci			     without the @goal_irq_mutex held. */
508c2ecf20Sopenharmony_ci	u64 ctx;
518c2ecf20Sopenharmony_ci};
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistruct vmw_user_fence {
548c2ecf20Sopenharmony_ci	struct ttm_base_object base;
558c2ecf20Sopenharmony_ci	struct vmw_fence_obj fence;
568c2ecf20Sopenharmony_ci};
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci/**
598c2ecf20Sopenharmony_ci * struct vmw_event_fence_action - fence action that delivers a drm event.
608c2ecf20Sopenharmony_ci *
618c2ecf20Sopenharmony_ci * @e: A struct drm_pending_event that controls the event delivery.
628c2ecf20Sopenharmony_ci * @action: A struct vmw_fence_action to hook up to a fence.
638c2ecf20Sopenharmony_ci * @fence: A referenced pointer to the fence to keep it alive while @action
648c2ecf20Sopenharmony_ci * hangs on it.
658c2ecf20Sopenharmony_ci * @dev: Pointer to a struct drm_device so we can access the event stuff.
668c2ecf20Sopenharmony_ci * @kref: Both @e and @action has destructors, so we need to refcount.
678c2ecf20Sopenharmony_ci * @size: Size accounted for this object.
688c2ecf20Sopenharmony_ci * @tv_sec: If non-null, the variable pointed to will be assigned
698c2ecf20Sopenharmony_ci * current time tv_sec val when the fence signals.
708c2ecf20Sopenharmony_ci * @tv_usec: Must be set if @tv_sec is set, and the variable pointed to will
718c2ecf20Sopenharmony_ci * be assigned the current time tv_usec val when the fence signals.
728c2ecf20Sopenharmony_ci */
738c2ecf20Sopenharmony_cistruct vmw_event_fence_action {
748c2ecf20Sopenharmony_ci	struct vmw_fence_action action;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	struct drm_pending_event *event;
778c2ecf20Sopenharmony_ci	struct vmw_fence_obj *fence;
788c2ecf20Sopenharmony_ci	struct drm_device *dev;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	uint32_t *tv_sec;
818c2ecf20Sopenharmony_ci	uint32_t *tv_usec;
828c2ecf20Sopenharmony_ci};
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_cistatic struct vmw_fence_manager *
858c2ecf20Sopenharmony_cifman_from_fence(struct vmw_fence_obj *fence)
868c2ecf20Sopenharmony_ci{
878c2ecf20Sopenharmony_ci	return container_of(fence->base.lock, struct vmw_fence_manager, lock);
888c2ecf20Sopenharmony_ci}
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci/**
918c2ecf20Sopenharmony_ci * Note on fencing subsystem usage of irqs:
928c2ecf20Sopenharmony_ci * Typically the vmw_fences_update function is called
938c2ecf20Sopenharmony_ci *
948c2ecf20Sopenharmony_ci * a) When a new fence seqno has been submitted by the fifo code.
958c2ecf20Sopenharmony_ci * b) On-demand when we have waiters. Sleeping waiters will switch on the
968c2ecf20Sopenharmony_ci * ANY_FENCE irq and call vmw_fences_update function each time an ANY_FENCE
978c2ecf20Sopenharmony_ci * irq is received. When the last fence waiter is gone, that IRQ is masked
988c2ecf20Sopenharmony_ci * away.
998c2ecf20Sopenharmony_ci *
1008c2ecf20Sopenharmony_ci * In situations where there are no waiters and we don't submit any new fences,
1018c2ecf20Sopenharmony_ci * fence objects may not be signaled. This is perfectly OK, since there are
1028c2ecf20Sopenharmony_ci * no consumers of the signaled data, but that is NOT ok when there are fence
1038c2ecf20Sopenharmony_ci * actions attached to a fence. The fencing subsystem then makes use of the
1048c2ecf20Sopenharmony_ci * FENCE_GOAL irq and sets the fence goal seqno to that of the next fence
1058c2ecf20Sopenharmony_ci * which has an action attached, and each time vmw_fences_update is called,
1068c2ecf20Sopenharmony_ci * the subsystem makes sure the fence goal seqno is updated.
1078c2ecf20Sopenharmony_ci *
1088c2ecf20Sopenharmony_ci * The fence goal seqno irq is on as long as there are unsignaled fence
1098c2ecf20Sopenharmony_ci * objects with actions attached to them.
1108c2ecf20Sopenharmony_ci */
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_cistatic void vmw_fence_obj_destroy(struct dma_fence *f)
1138c2ecf20Sopenharmony_ci{
1148c2ecf20Sopenharmony_ci	struct vmw_fence_obj *fence =
1158c2ecf20Sopenharmony_ci		container_of(f, struct vmw_fence_obj, base);
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	struct vmw_fence_manager *fman = fman_from_fence(fence);
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	spin_lock(&fman->lock);
1208c2ecf20Sopenharmony_ci	list_del_init(&fence->head);
1218c2ecf20Sopenharmony_ci	--fman->num_fence_objects;
1228c2ecf20Sopenharmony_ci	spin_unlock(&fman->lock);
1238c2ecf20Sopenharmony_ci	fence->destroy(fence);
1248c2ecf20Sopenharmony_ci}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_cistatic const char *vmw_fence_get_driver_name(struct dma_fence *f)
1278c2ecf20Sopenharmony_ci{
1288c2ecf20Sopenharmony_ci	return "vmwgfx";
1298c2ecf20Sopenharmony_ci}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_cistatic const char *vmw_fence_get_timeline_name(struct dma_fence *f)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	return "svga";
1348c2ecf20Sopenharmony_ci}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_cistatic bool vmw_fence_enable_signaling(struct dma_fence *f)
1378c2ecf20Sopenharmony_ci{
1388c2ecf20Sopenharmony_ci	struct vmw_fence_obj *fence =
1398c2ecf20Sopenharmony_ci		container_of(f, struct vmw_fence_obj, base);
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	struct vmw_fence_manager *fman = fman_from_fence(fence);
1428c2ecf20Sopenharmony_ci	struct vmw_private *dev_priv = fman->dev_priv;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	u32 *fifo_mem = dev_priv->mmio_virt;
1458c2ecf20Sopenharmony_ci	u32 seqno = vmw_mmio_read(fifo_mem + SVGA_FIFO_FENCE);
1468c2ecf20Sopenharmony_ci	if (seqno - fence->base.seqno < VMW_FENCE_WRAP)
1478c2ecf20Sopenharmony_ci		return false;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	vmw_fifo_ping_host(dev_priv, SVGA_SYNC_GENERIC);
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	return true;
1528c2ecf20Sopenharmony_ci}
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_cistruct vmwgfx_wait_cb {
1558c2ecf20Sopenharmony_ci	struct dma_fence_cb base;
1568c2ecf20Sopenharmony_ci	struct task_struct *task;
1578c2ecf20Sopenharmony_ci};
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_cistatic void
1608c2ecf20Sopenharmony_civmwgfx_wait_cb(struct dma_fence *fence, struct dma_fence_cb *cb)
1618c2ecf20Sopenharmony_ci{
1628c2ecf20Sopenharmony_ci	struct vmwgfx_wait_cb *wait =
1638c2ecf20Sopenharmony_ci		container_of(cb, struct vmwgfx_wait_cb, base);
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	wake_up_process(wait->task);
1668c2ecf20Sopenharmony_ci}
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_cistatic void __vmw_fences_update(struct vmw_fence_manager *fman);
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_cistatic long vmw_fence_wait(struct dma_fence *f, bool intr, signed long timeout)
1718c2ecf20Sopenharmony_ci{
1728c2ecf20Sopenharmony_ci	struct vmw_fence_obj *fence =
1738c2ecf20Sopenharmony_ci		container_of(f, struct vmw_fence_obj, base);
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	struct vmw_fence_manager *fman = fman_from_fence(fence);
1768c2ecf20Sopenharmony_ci	struct vmw_private *dev_priv = fman->dev_priv;
1778c2ecf20Sopenharmony_ci	struct vmwgfx_wait_cb cb;
1788c2ecf20Sopenharmony_ci	long ret = timeout;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	if (likely(vmw_fence_obj_signaled(fence)))
1818c2ecf20Sopenharmony_ci		return timeout;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	vmw_fifo_ping_host(dev_priv, SVGA_SYNC_GENERIC);
1848c2ecf20Sopenharmony_ci	vmw_seqno_waiter_add(dev_priv);
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	spin_lock(f->lock);
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &f->flags))
1898c2ecf20Sopenharmony_ci		goto out;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	if (intr && signal_pending(current)) {
1928c2ecf20Sopenharmony_ci		ret = -ERESTARTSYS;
1938c2ecf20Sopenharmony_ci		goto out;
1948c2ecf20Sopenharmony_ci	}
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	cb.base.func = vmwgfx_wait_cb;
1978c2ecf20Sopenharmony_ci	cb.task = current;
1988c2ecf20Sopenharmony_ci	list_add(&cb.base.node, &f->cb_list);
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	for (;;) {
2018c2ecf20Sopenharmony_ci		__vmw_fences_update(fman);
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci		/*
2048c2ecf20Sopenharmony_ci		 * We can use the barrier free __set_current_state() since
2058c2ecf20Sopenharmony_ci		 * DMA_FENCE_FLAG_SIGNALED_BIT + wakeup is protected by the
2068c2ecf20Sopenharmony_ci		 * fence spinlock.
2078c2ecf20Sopenharmony_ci		 */
2088c2ecf20Sopenharmony_ci		if (intr)
2098c2ecf20Sopenharmony_ci			__set_current_state(TASK_INTERRUPTIBLE);
2108c2ecf20Sopenharmony_ci		else
2118c2ecf20Sopenharmony_ci			__set_current_state(TASK_UNINTERRUPTIBLE);
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci		if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &f->flags)) {
2148c2ecf20Sopenharmony_ci			if (ret == 0 && timeout > 0)
2158c2ecf20Sopenharmony_ci				ret = 1;
2168c2ecf20Sopenharmony_ci			break;
2178c2ecf20Sopenharmony_ci		}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci		if (intr && signal_pending(current)) {
2208c2ecf20Sopenharmony_ci			ret = -ERESTARTSYS;
2218c2ecf20Sopenharmony_ci			break;
2228c2ecf20Sopenharmony_ci		}
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci		if (ret == 0)
2258c2ecf20Sopenharmony_ci			break;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci		spin_unlock(f->lock);
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci		ret = schedule_timeout(ret);
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci		spin_lock(f->lock);
2328c2ecf20Sopenharmony_ci	}
2338c2ecf20Sopenharmony_ci	__set_current_state(TASK_RUNNING);
2348c2ecf20Sopenharmony_ci	if (!list_empty(&cb.base.node))
2358c2ecf20Sopenharmony_ci		list_del(&cb.base.node);
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ciout:
2388c2ecf20Sopenharmony_ci	spin_unlock(f->lock);
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	vmw_seqno_waiter_remove(dev_priv);
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	return ret;
2438c2ecf20Sopenharmony_ci}
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_cistatic const struct dma_fence_ops vmw_fence_ops = {
2468c2ecf20Sopenharmony_ci	.get_driver_name = vmw_fence_get_driver_name,
2478c2ecf20Sopenharmony_ci	.get_timeline_name = vmw_fence_get_timeline_name,
2488c2ecf20Sopenharmony_ci	.enable_signaling = vmw_fence_enable_signaling,
2498c2ecf20Sopenharmony_ci	.wait = vmw_fence_wait,
2508c2ecf20Sopenharmony_ci	.release = vmw_fence_obj_destroy,
2518c2ecf20Sopenharmony_ci};
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci/**
2558c2ecf20Sopenharmony_ci * Execute signal actions on fences recently signaled.
2568c2ecf20Sopenharmony_ci * This is done from a workqueue so we don't have to execute
2578c2ecf20Sopenharmony_ci * signal actions from atomic context.
2588c2ecf20Sopenharmony_ci */
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_cistatic void vmw_fence_work_func(struct work_struct *work)
2618c2ecf20Sopenharmony_ci{
2628c2ecf20Sopenharmony_ci	struct vmw_fence_manager *fman =
2638c2ecf20Sopenharmony_ci		container_of(work, struct vmw_fence_manager, work);
2648c2ecf20Sopenharmony_ci	struct list_head list;
2658c2ecf20Sopenharmony_ci	struct vmw_fence_action *action, *next_action;
2668c2ecf20Sopenharmony_ci	bool seqno_valid;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	do {
2698c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&list);
2708c2ecf20Sopenharmony_ci		mutex_lock(&fman->goal_irq_mutex);
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci		spin_lock(&fman->lock);
2738c2ecf20Sopenharmony_ci		list_splice_init(&fman->cleanup_list, &list);
2748c2ecf20Sopenharmony_ci		seqno_valid = fman->seqno_valid;
2758c2ecf20Sopenharmony_ci		spin_unlock(&fman->lock);
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci		if (!seqno_valid && fman->goal_irq_on) {
2788c2ecf20Sopenharmony_ci			fman->goal_irq_on = false;
2798c2ecf20Sopenharmony_ci			vmw_goal_waiter_remove(fman->dev_priv);
2808c2ecf20Sopenharmony_ci		}
2818c2ecf20Sopenharmony_ci		mutex_unlock(&fman->goal_irq_mutex);
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci		if (list_empty(&list))
2848c2ecf20Sopenharmony_ci			return;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci		/*
2878c2ecf20Sopenharmony_ci		 * At this point, only we should be able to manipulate the
2888c2ecf20Sopenharmony_ci		 * list heads of the actions we have on the private list.
2898c2ecf20Sopenharmony_ci		 * hence fman::lock not held.
2908c2ecf20Sopenharmony_ci		 */
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci		list_for_each_entry_safe(action, next_action, &list, head) {
2938c2ecf20Sopenharmony_ci			list_del_init(&action->head);
2948c2ecf20Sopenharmony_ci			if (action->cleanup)
2958c2ecf20Sopenharmony_ci				action->cleanup(action);
2968c2ecf20Sopenharmony_ci		}
2978c2ecf20Sopenharmony_ci	} while (1);
2988c2ecf20Sopenharmony_ci}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_cistruct vmw_fence_manager *vmw_fence_manager_init(struct vmw_private *dev_priv)
3018c2ecf20Sopenharmony_ci{
3028c2ecf20Sopenharmony_ci	struct vmw_fence_manager *fman = kzalloc(sizeof(*fman), GFP_KERNEL);
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	if (unlikely(!fman))
3058c2ecf20Sopenharmony_ci		return NULL;
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	fman->dev_priv = dev_priv;
3088c2ecf20Sopenharmony_ci	spin_lock_init(&fman->lock);
3098c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&fman->fence_list);
3108c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&fman->cleanup_list);
3118c2ecf20Sopenharmony_ci	INIT_WORK(&fman->work, &vmw_fence_work_func);
3128c2ecf20Sopenharmony_ci	fman->fifo_down = true;
3138c2ecf20Sopenharmony_ci	fman->user_fence_size = ttm_round_pot(sizeof(struct vmw_user_fence)) +
3148c2ecf20Sopenharmony_ci		TTM_OBJ_EXTRA_SIZE;
3158c2ecf20Sopenharmony_ci	fman->fence_size = ttm_round_pot(sizeof(struct vmw_fence_obj));
3168c2ecf20Sopenharmony_ci	fman->event_fence_action_size =
3178c2ecf20Sopenharmony_ci		ttm_round_pot(sizeof(struct vmw_event_fence_action));
3188c2ecf20Sopenharmony_ci	mutex_init(&fman->goal_irq_mutex);
3198c2ecf20Sopenharmony_ci	fman->ctx = dma_fence_context_alloc(1);
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	return fman;
3228c2ecf20Sopenharmony_ci}
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_civoid vmw_fence_manager_takedown(struct vmw_fence_manager *fman)
3258c2ecf20Sopenharmony_ci{
3268c2ecf20Sopenharmony_ci	bool lists_empty;
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	(void) cancel_work_sync(&fman->work);
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	spin_lock(&fman->lock);
3318c2ecf20Sopenharmony_ci	lists_empty = list_empty(&fman->fence_list) &&
3328c2ecf20Sopenharmony_ci		list_empty(&fman->cleanup_list);
3338c2ecf20Sopenharmony_ci	spin_unlock(&fman->lock);
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	BUG_ON(!lists_empty);
3368c2ecf20Sopenharmony_ci	kfree(fman);
3378c2ecf20Sopenharmony_ci}
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_cistatic int vmw_fence_obj_init(struct vmw_fence_manager *fman,
3408c2ecf20Sopenharmony_ci			      struct vmw_fence_obj *fence, u32 seqno,
3418c2ecf20Sopenharmony_ci			      void (*destroy) (struct vmw_fence_obj *fence))
3428c2ecf20Sopenharmony_ci{
3438c2ecf20Sopenharmony_ci	int ret = 0;
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	dma_fence_init(&fence->base, &vmw_fence_ops, &fman->lock,
3468c2ecf20Sopenharmony_ci		       fman->ctx, seqno);
3478c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&fence->seq_passed_actions);
3488c2ecf20Sopenharmony_ci	fence->destroy = destroy;
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	spin_lock(&fman->lock);
3518c2ecf20Sopenharmony_ci	if (unlikely(fman->fifo_down)) {
3528c2ecf20Sopenharmony_ci		ret = -EBUSY;
3538c2ecf20Sopenharmony_ci		goto out_unlock;
3548c2ecf20Sopenharmony_ci	}
3558c2ecf20Sopenharmony_ci	list_add_tail(&fence->head, &fman->fence_list);
3568c2ecf20Sopenharmony_ci	++fman->num_fence_objects;
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ciout_unlock:
3598c2ecf20Sopenharmony_ci	spin_unlock(&fman->lock);
3608c2ecf20Sopenharmony_ci	return ret;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci}
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_cistatic void vmw_fences_perform_actions(struct vmw_fence_manager *fman,
3658c2ecf20Sopenharmony_ci				struct list_head *list)
3668c2ecf20Sopenharmony_ci{
3678c2ecf20Sopenharmony_ci	struct vmw_fence_action *action, *next_action;
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	list_for_each_entry_safe(action, next_action, list, head) {
3708c2ecf20Sopenharmony_ci		list_del_init(&action->head);
3718c2ecf20Sopenharmony_ci		fman->pending_actions[action->type]--;
3728c2ecf20Sopenharmony_ci		if (action->seq_passed != NULL)
3738c2ecf20Sopenharmony_ci			action->seq_passed(action);
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci		/*
3768c2ecf20Sopenharmony_ci		 * Add the cleanup action to the cleanup list so that
3778c2ecf20Sopenharmony_ci		 * it will be performed by a worker task.
3788c2ecf20Sopenharmony_ci		 */
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci		list_add_tail(&action->head, &fman->cleanup_list);
3818c2ecf20Sopenharmony_ci	}
3828c2ecf20Sopenharmony_ci}
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci/**
3858c2ecf20Sopenharmony_ci * vmw_fence_goal_new_locked - Figure out a new device fence goal
3868c2ecf20Sopenharmony_ci * seqno if needed.
3878c2ecf20Sopenharmony_ci *
3888c2ecf20Sopenharmony_ci * @fman: Pointer to a fence manager.
3898c2ecf20Sopenharmony_ci * @passed_seqno: The seqno the device currently signals as passed.
3908c2ecf20Sopenharmony_ci *
3918c2ecf20Sopenharmony_ci * This function should be called with the fence manager lock held.
3928c2ecf20Sopenharmony_ci * It is typically called when we have a new passed_seqno, and
3938c2ecf20Sopenharmony_ci * we might need to update the fence goal. It checks to see whether
3948c2ecf20Sopenharmony_ci * the current fence goal has already passed, and, in that case,
3958c2ecf20Sopenharmony_ci * scans through all unsignaled fences to get the next fence object with an
3968c2ecf20Sopenharmony_ci * action attached, and sets the seqno of that fence as a new fence goal.
3978c2ecf20Sopenharmony_ci *
3988c2ecf20Sopenharmony_ci * returns true if the device goal seqno was updated. False otherwise.
3998c2ecf20Sopenharmony_ci */
4008c2ecf20Sopenharmony_cistatic bool vmw_fence_goal_new_locked(struct vmw_fence_manager *fman,
4018c2ecf20Sopenharmony_ci				      u32 passed_seqno)
4028c2ecf20Sopenharmony_ci{
4038c2ecf20Sopenharmony_ci	u32 goal_seqno;
4048c2ecf20Sopenharmony_ci	u32 *fifo_mem;
4058c2ecf20Sopenharmony_ci	struct vmw_fence_obj *fence;
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	if (likely(!fman->seqno_valid))
4088c2ecf20Sopenharmony_ci		return false;
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	fifo_mem = fman->dev_priv->mmio_virt;
4118c2ecf20Sopenharmony_ci	goal_seqno = vmw_mmio_read(fifo_mem + SVGA_FIFO_FENCE_GOAL);
4128c2ecf20Sopenharmony_ci	if (likely(passed_seqno - goal_seqno >= VMW_FENCE_WRAP))
4138c2ecf20Sopenharmony_ci		return false;
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	fman->seqno_valid = false;
4168c2ecf20Sopenharmony_ci	list_for_each_entry(fence, &fman->fence_list, head) {
4178c2ecf20Sopenharmony_ci		if (!list_empty(&fence->seq_passed_actions)) {
4188c2ecf20Sopenharmony_ci			fman->seqno_valid = true;
4198c2ecf20Sopenharmony_ci			vmw_mmio_write(fence->base.seqno,
4208c2ecf20Sopenharmony_ci				       fifo_mem + SVGA_FIFO_FENCE_GOAL);
4218c2ecf20Sopenharmony_ci			break;
4228c2ecf20Sopenharmony_ci		}
4238c2ecf20Sopenharmony_ci	}
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	return true;
4268c2ecf20Sopenharmony_ci}
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci/**
4308c2ecf20Sopenharmony_ci * vmw_fence_goal_check_locked - Replace the device fence goal seqno if
4318c2ecf20Sopenharmony_ci * needed.
4328c2ecf20Sopenharmony_ci *
4338c2ecf20Sopenharmony_ci * @fence: Pointer to a struct vmw_fence_obj the seqno of which should be
4348c2ecf20Sopenharmony_ci * considered as a device fence goal.
4358c2ecf20Sopenharmony_ci *
4368c2ecf20Sopenharmony_ci * This function should be called with the fence manager lock held.
4378c2ecf20Sopenharmony_ci * It is typically called when an action has been attached to a fence to
4388c2ecf20Sopenharmony_ci * check whether the seqno of that fence should be used for a fence
4398c2ecf20Sopenharmony_ci * goal interrupt. This is typically needed if the current fence goal is
4408c2ecf20Sopenharmony_ci * invalid, or has a higher seqno than that of the current fence object.
4418c2ecf20Sopenharmony_ci *
4428c2ecf20Sopenharmony_ci * returns true if the device goal seqno was updated. False otherwise.
4438c2ecf20Sopenharmony_ci */
4448c2ecf20Sopenharmony_cistatic bool vmw_fence_goal_check_locked(struct vmw_fence_obj *fence)
4458c2ecf20Sopenharmony_ci{
4468c2ecf20Sopenharmony_ci	struct vmw_fence_manager *fman = fman_from_fence(fence);
4478c2ecf20Sopenharmony_ci	u32 goal_seqno;
4488c2ecf20Sopenharmony_ci	u32 *fifo_mem;
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	if (dma_fence_is_signaled_locked(&fence->base))
4518c2ecf20Sopenharmony_ci		return false;
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	fifo_mem = fman->dev_priv->mmio_virt;
4548c2ecf20Sopenharmony_ci	goal_seqno = vmw_mmio_read(fifo_mem + SVGA_FIFO_FENCE_GOAL);
4558c2ecf20Sopenharmony_ci	if (likely(fman->seqno_valid &&
4568c2ecf20Sopenharmony_ci		   goal_seqno - fence->base.seqno < VMW_FENCE_WRAP))
4578c2ecf20Sopenharmony_ci		return false;
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	vmw_mmio_write(fence->base.seqno, fifo_mem + SVGA_FIFO_FENCE_GOAL);
4608c2ecf20Sopenharmony_ci	fman->seqno_valid = true;
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	return true;
4638c2ecf20Sopenharmony_ci}
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_cistatic void __vmw_fences_update(struct vmw_fence_manager *fman)
4668c2ecf20Sopenharmony_ci{
4678c2ecf20Sopenharmony_ci	struct vmw_fence_obj *fence, *next_fence;
4688c2ecf20Sopenharmony_ci	struct list_head action_list;
4698c2ecf20Sopenharmony_ci	bool needs_rerun;
4708c2ecf20Sopenharmony_ci	uint32_t seqno, new_seqno;
4718c2ecf20Sopenharmony_ci	u32 *fifo_mem = fman->dev_priv->mmio_virt;
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	seqno = vmw_mmio_read(fifo_mem + SVGA_FIFO_FENCE);
4748c2ecf20Sopenharmony_cirerun:
4758c2ecf20Sopenharmony_ci	list_for_each_entry_safe(fence, next_fence, &fman->fence_list, head) {
4768c2ecf20Sopenharmony_ci		if (seqno - fence->base.seqno < VMW_FENCE_WRAP) {
4778c2ecf20Sopenharmony_ci			list_del_init(&fence->head);
4788c2ecf20Sopenharmony_ci			dma_fence_signal_locked(&fence->base);
4798c2ecf20Sopenharmony_ci			INIT_LIST_HEAD(&action_list);
4808c2ecf20Sopenharmony_ci			list_splice_init(&fence->seq_passed_actions,
4818c2ecf20Sopenharmony_ci					 &action_list);
4828c2ecf20Sopenharmony_ci			vmw_fences_perform_actions(fman, &action_list);
4838c2ecf20Sopenharmony_ci		} else
4848c2ecf20Sopenharmony_ci			break;
4858c2ecf20Sopenharmony_ci	}
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci	/*
4888c2ecf20Sopenharmony_ci	 * Rerun if the fence goal seqno was updated, and the
4898c2ecf20Sopenharmony_ci	 * hardware might have raced with that update, so that
4908c2ecf20Sopenharmony_ci	 * we missed a fence_goal irq.
4918c2ecf20Sopenharmony_ci	 */
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	needs_rerun = vmw_fence_goal_new_locked(fman, seqno);
4948c2ecf20Sopenharmony_ci	if (unlikely(needs_rerun)) {
4958c2ecf20Sopenharmony_ci		new_seqno = vmw_mmio_read(fifo_mem + SVGA_FIFO_FENCE);
4968c2ecf20Sopenharmony_ci		if (new_seqno != seqno) {
4978c2ecf20Sopenharmony_ci			seqno = new_seqno;
4988c2ecf20Sopenharmony_ci			goto rerun;
4998c2ecf20Sopenharmony_ci		}
5008c2ecf20Sopenharmony_ci	}
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	if (!list_empty(&fman->cleanup_list))
5038c2ecf20Sopenharmony_ci		(void) schedule_work(&fman->work);
5048c2ecf20Sopenharmony_ci}
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_civoid vmw_fences_update(struct vmw_fence_manager *fman)
5078c2ecf20Sopenharmony_ci{
5088c2ecf20Sopenharmony_ci	spin_lock(&fman->lock);
5098c2ecf20Sopenharmony_ci	__vmw_fences_update(fman);
5108c2ecf20Sopenharmony_ci	spin_unlock(&fman->lock);
5118c2ecf20Sopenharmony_ci}
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_cibool vmw_fence_obj_signaled(struct vmw_fence_obj *fence)
5148c2ecf20Sopenharmony_ci{
5158c2ecf20Sopenharmony_ci	struct vmw_fence_manager *fman = fman_from_fence(fence);
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->base.flags))
5188c2ecf20Sopenharmony_ci		return true;
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci	vmw_fences_update(fman);
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci	return dma_fence_is_signaled(&fence->base);
5238c2ecf20Sopenharmony_ci}
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ciint vmw_fence_obj_wait(struct vmw_fence_obj *fence, bool lazy,
5268c2ecf20Sopenharmony_ci		       bool interruptible, unsigned long timeout)
5278c2ecf20Sopenharmony_ci{
5288c2ecf20Sopenharmony_ci	long ret = dma_fence_wait_timeout(&fence->base, interruptible, timeout);
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	if (likely(ret > 0))
5318c2ecf20Sopenharmony_ci		return 0;
5328c2ecf20Sopenharmony_ci	else if (ret == 0)
5338c2ecf20Sopenharmony_ci		return -EBUSY;
5348c2ecf20Sopenharmony_ci	else
5358c2ecf20Sopenharmony_ci		return ret;
5368c2ecf20Sopenharmony_ci}
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_civoid vmw_fence_obj_flush(struct vmw_fence_obj *fence)
5398c2ecf20Sopenharmony_ci{
5408c2ecf20Sopenharmony_ci	struct vmw_private *dev_priv = fman_from_fence(fence)->dev_priv;
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	vmw_fifo_ping_host(dev_priv, SVGA_SYNC_GENERIC);
5438c2ecf20Sopenharmony_ci}
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_cistatic void vmw_fence_destroy(struct vmw_fence_obj *fence)
5468c2ecf20Sopenharmony_ci{
5478c2ecf20Sopenharmony_ci	dma_fence_free(&fence->base);
5488c2ecf20Sopenharmony_ci}
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ciint vmw_fence_create(struct vmw_fence_manager *fman,
5518c2ecf20Sopenharmony_ci		     uint32_t seqno,
5528c2ecf20Sopenharmony_ci		     struct vmw_fence_obj **p_fence)
5538c2ecf20Sopenharmony_ci{
5548c2ecf20Sopenharmony_ci	struct vmw_fence_obj *fence;
5558c2ecf20Sopenharmony_ci 	int ret;
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	fence = kzalloc(sizeof(*fence), GFP_KERNEL);
5588c2ecf20Sopenharmony_ci	if (unlikely(!fence))
5598c2ecf20Sopenharmony_ci		return -ENOMEM;
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	ret = vmw_fence_obj_init(fman, fence, seqno,
5628c2ecf20Sopenharmony_ci				 vmw_fence_destroy);
5638c2ecf20Sopenharmony_ci	if (unlikely(ret != 0))
5648c2ecf20Sopenharmony_ci		goto out_err_init;
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	*p_fence = fence;
5678c2ecf20Sopenharmony_ci	return 0;
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ciout_err_init:
5708c2ecf20Sopenharmony_ci	kfree(fence);
5718c2ecf20Sopenharmony_ci	return ret;
5728c2ecf20Sopenharmony_ci}
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_cistatic void vmw_user_fence_destroy(struct vmw_fence_obj *fence)
5768c2ecf20Sopenharmony_ci{
5778c2ecf20Sopenharmony_ci	struct vmw_user_fence *ufence =
5788c2ecf20Sopenharmony_ci		container_of(fence, struct vmw_user_fence, fence);
5798c2ecf20Sopenharmony_ci	struct vmw_fence_manager *fman = fman_from_fence(fence);
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci	ttm_base_object_kfree(ufence, base);
5828c2ecf20Sopenharmony_ci	/*
5838c2ecf20Sopenharmony_ci	 * Free kernel space accounting.
5848c2ecf20Sopenharmony_ci	 */
5858c2ecf20Sopenharmony_ci	ttm_mem_global_free(vmw_mem_glob(fman->dev_priv),
5868c2ecf20Sopenharmony_ci			    fman->user_fence_size);
5878c2ecf20Sopenharmony_ci}
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_cistatic void vmw_user_fence_base_release(struct ttm_base_object **p_base)
5908c2ecf20Sopenharmony_ci{
5918c2ecf20Sopenharmony_ci	struct ttm_base_object *base = *p_base;
5928c2ecf20Sopenharmony_ci	struct vmw_user_fence *ufence =
5938c2ecf20Sopenharmony_ci		container_of(base, struct vmw_user_fence, base);
5948c2ecf20Sopenharmony_ci	struct vmw_fence_obj *fence = &ufence->fence;
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci	*p_base = NULL;
5978c2ecf20Sopenharmony_ci	vmw_fence_obj_unreference(&fence);
5988c2ecf20Sopenharmony_ci}
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ciint vmw_user_fence_create(struct drm_file *file_priv,
6018c2ecf20Sopenharmony_ci			  struct vmw_fence_manager *fman,
6028c2ecf20Sopenharmony_ci			  uint32_t seqno,
6038c2ecf20Sopenharmony_ci			  struct vmw_fence_obj **p_fence,
6048c2ecf20Sopenharmony_ci			  uint32_t *p_handle)
6058c2ecf20Sopenharmony_ci{
6068c2ecf20Sopenharmony_ci	struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
6078c2ecf20Sopenharmony_ci	struct vmw_user_fence *ufence;
6088c2ecf20Sopenharmony_ci	struct vmw_fence_obj *tmp;
6098c2ecf20Sopenharmony_ci	struct ttm_mem_global *mem_glob = vmw_mem_glob(fman->dev_priv);
6108c2ecf20Sopenharmony_ci	struct ttm_operation_ctx ctx = {
6118c2ecf20Sopenharmony_ci		.interruptible = false,
6128c2ecf20Sopenharmony_ci		.no_wait_gpu = false
6138c2ecf20Sopenharmony_ci	};
6148c2ecf20Sopenharmony_ci	int ret;
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci	/*
6178c2ecf20Sopenharmony_ci	 * Kernel memory space accounting, since this object may
6188c2ecf20Sopenharmony_ci	 * be created by a user-space request.
6198c2ecf20Sopenharmony_ci	 */
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci	ret = ttm_mem_global_alloc(mem_glob, fman->user_fence_size,
6228c2ecf20Sopenharmony_ci				   &ctx);
6238c2ecf20Sopenharmony_ci	if (unlikely(ret != 0))
6248c2ecf20Sopenharmony_ci		return ret;
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_ci	ufence = kzalloc(sizeof(*ufence), GFP_KERNEL);
6278c2ecf20Sopenharmony_ci	if (unlikely(!ufence)) {
6288c2ecf20Sopenharmony_ci		ret = -ENOMEM;
6298c2ecf20Sopenharmony_ci		goto out_no_object;
6308c2ecf20Sopenharmony_ci	}
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci	ret = vmw_fence_obj_init(fman, &ufence->fence, seqno,
6338c2ecf20Sopenharmony_ci				 vmw_user_fence_destroy);
6348c2ecf20Sopenharmony_ci	if (unlikely(ret != 0)) {
6358c2ecf20Sopenharmony_ci		kfree(ufence);
6368c2ecf20Sopenharmony_ci		goto out_no_object;
6378c2ecf20Sopenharmony_ci	}
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci	/*
6408c2ecf20Sopenharmony_ci	 * The base object holds a reference which is freed in
6418c2ecf20Sopenharmony_ci	 * vmw_user_fence_base_release.
6428c2ecf20Sopenharmony_ci	 */
6438c2ecf20Sopenharmony_ci	tmp = vmw_fence_obj_reference(&ufence->fence);
6448c2ecf20Sopenharmony_ci	ret = ttm_base_object_init(tfile, &ufence->base, false,
6458c2ecf20Sopenharmony_ci				   VMW_RES_FENCE,
6468c2ecf20Sopenharmony_ci				   &vmw_user_fence_base_release, NULL);
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ci	if (unlikely(ret != 0)) {
6508c2ecf20Sopenharmony_ci		/*
6518c2ecf20Sopenharmony_ci		 * Free the base object's reference
6528c2ecf20Sopenharmony_ci		 */
6538c2ecf20Sopenharmony_ci		vmw_fence_obj_unreference(&tmp);
6548c2ecf20Sopenharmony_ci		goto out_err;
6558c2ecf20Sopenharmony_ci	}
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci	*p_fence = &ufence->fence;
6588c2ecf20Sopenharmony_ci	*p_handle = ufence->base.handle;
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci	return 0;
6618c2ecf20Sopenharmony_ciout_err:
6628c2ecf20Sopenharmony_ci	tmp = &ufence->fence;
6638c2ecf20Sopenharmony_ci	vmw_fence_obj_unreference(&tmp);
6648c2ecf20Sopenharmony_ciout_no_object:
6658c2ecf20Sopenharmony_ci	ttm_mem_global_free(mem_glob, fman->user_fence_size);
6668c2ecf20Sopenharmony_ci	return ret;
6678c2ecf20Sopenharmony_ci}
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_ci/**
6718c2ecf20Sopenharmony_ci * vmw_wait_dma_fence - Wait for a dma fence
6728c2ecf20Sopenharmony_ci *
6738c2ecf20Sopenharmony_ci * @fman: pointer to a fence manager
6748c2ecf20Sopenharmony_ci * @fence: DMA fence to wait on
6758c2ecf20Sopenharmony_ci *
6768c2ecf20Sopenharmony_ci * This function handles the case when the fence is actually a fence
6778c2ecf20Sopenharmony_ci * array.  If that's the case, it'll wait on each of the child fence
6788c2ecf20Sopenharmony_ci */
6798c2ecf20Sopenharmony_ciint vmw_wait_dma_fence(struct vmw_fence_manager *fman,
6808c2ecf20Sopenharmony_ci		       struct dma_fence *fence)
6818c2ecf20Sopenharmony_ci{
6828c2ecf20Sopenharmony_ci	struct dma_fence_array *fence_array;
6838c2ecf20Sopenharmony_ci	int ret = 0;
6848c2ecf20Sopenharmony_ci	int i;
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_ci	if (dma_fence_is_signaled(fence))
6888c2ecf20Sopenharmony_ci		return 0;
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci	if (!dma_fence_is_array(fence))
6918c2ecf20Sopenharmony_ci		return dma_fence_wait(fence, true);
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci	/* From i915: Note that if the fence-array was created in
6948c2ecf20Sopenharmony_ci	 * signal-on-any mode, we should *not* decompose it into its individual
6958c2ecf20Sopenharmony_ci	 * fences. However, we don't currently store which mode the fence-array
6968c2ecf20Sopenharmony_ci	 * is operating in. Fortunately, the only user of signal-on-any is
6978c2ecf20Sopenharmony_ci	 * private to amdgpu and we should not see any incoming fence-array
6988c2ecf20Sopenharmony_ci	 * from sync-file being in signal-on-any mode.
6998c2ecf20Sopenharmony_ci	 */
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ci	fence_array = to_dma_fence_array(fence);
7028c2ecf20Sopenharmony_ci	for (i = 0; i < fence_array->num_fences; i++) {
7038c2ecf20Sopenharmony_ci		struct dma_fence *child = fence_array->fences[i];
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci		ret = dma_fence_wait(child, true);
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci		if (ret < 0)
7088c2ecf20Sopenharmony_ci			return ret;
7098c2ecf20Sopenharmony_ci	}
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci	return 0;
7128c2ecf20Sopenharmony_ci}
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci/**
7168c2ecf20Sopenharmony_ci * vmw_fence_fifo_down - signal all unsignaled fence objects.
7178c2ecf20Sopenharmony_ci */
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_civoid vmw_fence_fifo_down(struct vmw_fence_manager *fman)
7208c2ecf20Sopenharmony_ci{
7218c2ecf20Sopenharmony_ci	struct list_head action_list;
7228c2ecf20Sopenharmony_ci	int ret;
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci	/*
7258c2ecf20Sopenharmony_ci	 * The list may be altered while we traverse it, so always
7268c2ecf20Sopenharmony_ci	 * restart when we've released the fman->lock.
7278c2ecf20Sopenharmony_ci	 */
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ci	spin_lock(&fman->lock);
7308c2ecf20Sopenharmony_ci	fman->fifo_down = true;
7318c2ecf20Sopenharmony_ci	while (!list_empty(&fman->fence_list)) {
7328c2ecf20Sopenharmony_ci		struct vmw_fence_obj *fence =
7338c2ecf20Sopenharmony_ci			list_entry(fman->fence_list.prev, struct vmw_fence_obj,
7348c2ecf20Sopenharmony_ci				   head);
7358c2ecf20Sopenharmony_ci		dma_fence_get(&fence->base);
7368c2ecf20Sopenharmony_ci		spin_unlock(&fman->lock);
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_ci		ret = vmw_fence_obj_wait(fence, false, false,
7398c2ecf20Sopenharmony_ci					 VMW_FENCE_WAIT_TIMEOUT);
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci		if (unlikely(ret != 0)) {
7428c2ecf20Sopenharmony_ci			list_del_init(&fence->head);
7438c2ecf20Sopenharmony_ci			dma_fence_signal(&fence->base);
7448c2ecf20Sopenharmony_ci			INIT_LIST_HEAD(&action_list);
7458c2ecf20Sopenharmony_ci			list_splice_init(&fence->seq_passed_actions,
7468c2ecf20Sopenharmony_ci					 &action_list);
7478c2ecf20Sopenharmony_ci			vmw_fences_perform_actions(fman, &action_list);
7488c2ecf20Sopenharmony_ci		}
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci		BUG_ON(!list_empty(&fence->head));
7518c2ecf20Sopenharmony_ci		dma_fence_put(&fence->base);
7528c2ecf20Sopenharmony_ci		spin_lock(&fman->lock);
7538c2ecf20Sopenharmony_ci	}
7548c2ecf20Sopenharmony_ci	spin_unlock(&fman->lock);
7558c2ecf20Sopenharmony_ci}
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_civoid vmw_fence_fifo_up(struct vmw_fence_manager *fman)
7588c2ecf20Sopenharmony_ci{
7598c2ecf20Sopenharmony_ci	spin_lock(&fman->lock);
7608c2ecf20Sopenharmony_ci	fman->fifo_down = false;
7618c2ecf20Sopenharmony_ci	spin_unlock(&fman->lock);
7628c2ecf20Sopenharmony_ci}
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_ci/**
7668c2ecf20Sopenharmony_ci * vmw_fence_obj_lookup - Look up a user-space fence object
7678c2ecf20Sopenharmony_ci *
7688c2ecf20Sopenharmony_ci * @tfile: A struct ttm_object_file identifying the caller.
7698c2ecf20Sopenharmony_ci * @handle: A handle identifying the fence object.
7708c2ecf20Sopenharmony_ci * @return: A struct vmw_user_fence base ttm object on success or
7718c2ecf20Sopenharmony_ci * an error pointer on failure.
7728c2ecf20Sopenharmony_ci *
7738c2ecf20Sopenharmony_ci * The fence object is looked up and type-checked. The caller needs
7748c2ecf20Sopenharmony_ci * to have opened the fence object first, but since that happens on
7758c2ecf20Sopenharmony_ci * creation and fence objects aren't shareable, that's not an
7768c2ecf20Sopenharmony_ci * issue currently.
7778c2ecf20Sopenharmony_ci */
7788c2ecf20Sopenharmony_cistatic struct ttm_base_object *
7798c2ecf20Sopenharmony_civmw_fence_obj_lookup(struct ttm_object_file *tfile, u32 handle)
7808c2ecf20Sopenharmony_ci{
7818c2ecf20Sopenharmony_ci	struct ttm_base_object *base = ttm_base_object_lookup(tfile, handle);
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci	if (!base) {
7848c2ecf20Sopenharmony_ci		pr_err("Invalid fence object handle 0x%08lx.\n",
7858c2ecf20Sopenharmony_ci		       (unsigned long)handle);
7868c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
7878c2ecf20Sopenharmony_ci	}
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci	if (base->refcount_release != vmw_user_fence_base_release) {
7908c2ecf20Sopenharmony_ci		pr_err("Invalid fence object handle 0x%08lx.\n",
7918c2ecf20Sopenharmony_ci		       (unsigned long)handle);
7928c2ecf20Sopenharmony_ci		ttm_base_object_unref(&base);
7938c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
7948c2ecf20Sopenharmony_ci	}
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_ci	return base;
7978c2ecf20Sopenharmony_ci}
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_ciint vmw_fence_obj_wait_ioctl(struct drm_device *dev, void *data,
8018c2ecf20Sopenharmony_ci			     struct drm_file *file_priv)
8028c2ecf20Sopenharmony_ci{
8038c2ecf20Sopenharmony_ci	struct drm_vmw_fence_wait_arg *arg =
8048c2ecf20Sopenharmony_ci	    (struct drm_vmw_fence_wait_arg *)data;
8058c2ecf20Sopenharmony_ci	unsigned long timeout;
8068c2ecf20Sopenharmony_ci	struct ttm_base_object *base;
8078c2ecf20Sopenharmony_ci	struct vmw_fence_obj *fence;
8088c2ecf20Sopenharmony_ci	struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
8098c2ecf20Sopenharmony_ci	int ret;
8108c2ecf20Sopenharmony_ci	uint64_t wait_timeout = ((uint64_t)arg->timeout_us * HZ);
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci	/*
8138c2ecf20Sopenharmony_ci	 * 64-bit division not present on 32-bit systems, so do an
8148c2ecf20Sopenharmony_ci	 * approximation. (Divide by 1000000).
8158c2ecf20Sopenharmony_ci	 */
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_ci	wait_timeout = (wait_timeout >> 20) + (wait_timeout >> 24) -
8188c2ecf20Sopenharmony_ci	  (wait_timeout >> 26);
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_ci	if (!arg->cookie_valid) {
8218c2ecf20Sopenharmony_ci		arg->cookie_valid = 1;
8228c2ecf20Sopenharmony_ci		arg->kernel_cookie = jiffies + wait_timeout;
8238c2ecf20Sopenharmony_ci	}
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_ci	base = vmw_fence_obj_lookup(tfile, arg->handle);
8268c2ecf20Sopenharmony_ci	if (IS_ERR(base))
8278c2ecf20Sopenharmony_ci		return PTR_ERR(base);
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_ci	fence = &(container_of(base, struct vmw_user_fence, base)->fence);
8308c2ecf20Sopenharmony_ci
8318c2ecf20Sopenharmony_ci	timeout = jiffies;
8328c2ecf20Sopenharmony_ci	if (time_after_eq(timeout, (unsigned long)arg->kernel_cookie)) {
8338c2ecf20Sopenharmony_ci		ret = ((vmw_fence_obj_signaled(fence)) ?
8348c2ecf20Sopenharmony_ci		       0 : -EBUSY);
8358c2ecf20Sopenharmony_ci		goto out;
8368c2ecf20Sopenharmony_ci	}
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_ci	timeout = (unsigned long)arg->kernel_cookie - timeout;
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci	ret = vmw_fence_obj_wait(fence, arg->lazy, true, timeout);
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_ciout:
8438c2ecf20Sopenharmony_ci	ttm_base_object_unref(&base);
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_ci	/*
8468c2ecf20Sopenharmony_ci	 * Optionally unref the fence object.
8478c2ecf20Sopenharmony_ci	 */
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_ci	if (ret == 0 && (arg->wait_options & DRM_VMW_WAIT_OPTION_UNREF))
8508c2ecf20Sopenharmony_ci		return ttm_ref_object_base_unref(tfile, arg->handle,
8518c2ecf20Sopenharmony_ci						 TTM_REF_USAGE);
8528c2ecf20Sopenharmony_ci	return ret;
8538c2ecf20Sopenharmony_ci}
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_ciint vmw_fence_obj_signaled_ioctl(struct drm_device *dev, void *data,
8568c2ecf20Sopenharmony_ci				 struct drm_file *file_priv)
8578c2ecf20Sopenharmony_ci{
8588c2ecf20Sopenharmony_ci	struct drm_vmw_fence_signaled_arg *arg =
8598c2ecf20Sopenharmony_ci		(struct drm_vmw_fence_signaled_arg *) data;
8608c2ecf20Sopenharmony_ci	struct ttm_base_object *base;
8618c2ecf20Sopenharmony_ci	struct vmw_fence_obj *fence;
8628c2ecf20Sopenharmony_ci	struct vmw_fence_manager *fman;
8638c2ecf20Sopenharmony_ci	struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
8648c2ecf20Sopenharmony_ci	struct vmw_private *dev_priv = vmw_priv(dev);
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_ci	base = vmw_fence_obj_lookup(tfile, arg->handle);
8678c2ecf20Sopenharmony_ci	if (IS_ERR(base))
8688c2ecf20Sopenharmony_ci		return PTR_ERR(base);
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ci	fence = &(container_of(base, struct vmw_user_fence, base)->fence);
8718c2ecf20Sopenharmony_ci	fman = fman_from_fence(fence);
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_ci	arg->signaled = vmw_fence_obj_signaled(fence);
8748c2ecf20Sopenharmony_ci
8758c2ecf20Sopenharmony_ci	arg->signaled_flags = arg->flags;
8768c2ecf20Sopenharmony_ci	spin_lock(&fman->lock);
8778c2ecf20Sopenharmony_ci	arg->passed_seqno = dev_priv->last_read_seqno;
8788c2ecf20Sopenharmony_ci	spin_unlock(&fman->lock);
8798c2ecf20Sopenharmony_ci
8808c2ecf20Sopenharmony_ci	ttm_base_object_unref(&base);
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_ci	return 0;
8838c2ecf20Sopenharmony_ci}
8848c2ecf20Sopenharmony_ci
8858c2ecf20Sopenharmony_ci
8868c2ecf20Sopenharmony_ciint vmw_fence_obj_unref_ioctl(struct drm_device *dev, void *data,
8878c2ecf20Sopenharmony_ci			      struct drm_file *file_priv)
8888c2ecf20Sopenharmony_ci{
8898c2ecf20Sopenharmony_ci	struct drm_vmw_fence_arg *arg =
8908c2ecf20Sopenharmony_ci		(struct drm_vmw_fence_arg *) data;
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_ci	return ttm_ref_object_base_unref(vmw_fpriv(file_priv)->tfile,
8938c2ecf20Sopenharmony_ci					 arg->handle,
8948c2ecf20Sopenharmony_ci					 TTM_REF_USAGE);
8958c2ecf20Sopenharmony_ci}
8968c2ecf20Sopenharmony_ci
8978c2ecf20Sopenharmony_ci/**
8988c2ecf20Sopenharmony_ci * vmw_event_fence_action_seq_passed
8998c2ecf20Sopenharmony_ci *
9008c2ecf20Sopenharmony_ci * @action: The struct vmw_fence_action embedded in a struct
9018c2ecf20Sopenharmony_ci * vmw_event_fence_action.
9028c2ecf20Sopenharmony_ci *
9038c2ecf20Sopenharmony_ci * This function is called when the seqno of the fence where @action is
9048c2ecf20Sopenharmony_ci * attached has passed. It queues the event on the submitter's event list.
9058c2ecf20Sopenharmony_ci * This function is always called from atomic context.
9068c2ecf20Sopenharmony_ci */
9078c2ecf20Sopenharmony_cistatic void vmw_event_fence_action_seq_passed(struct vmw_fence_action *action)
9088c2ecf20Sopenharmony_ci{
9098c2ecf20Sopenharmony_ci	struct vmw_event_fence_action *eaction =
9108c2ecf20Sopenharmony_ci		container_of(action, struct vmw_event_fence_action, action);
9118c2ecf20Sopenharmony_ci	struct drm_device *dev = eaction->dev;
9128c2ecf20Sopenharmony_ci	struct drm_pending_event *event = eaction->event;
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_ci	if (unlikely(event == NULL))
9158c2ecf20Sopenharmony_ci		return;
9168c2ecf20Sopenharmony_ci
9178c2ecf20Sopenharmony_ci	spin_lock_irq(&dev->event_lock);
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_ci	if (likely(eaction->tv_sec != NULL)) {
9208c2ecf20Sopenharmony_ci		struct timespec64 ts;
9218c2ecf20Sopenharmony_ci
9228c2ecf20Sopenharmony_ci		ktime_get_ts64(&ts);
9238c2ecf20Sopenharmony_ci		/* monotonic time, so no y2038 overflow */
9248c2ecf20Sopenharmony_ci		*eaction->tv_sec = ts.tv_sec;
9258c2ecf20Sopenharmony_ci		*eaction->tv_usec = ts.tv_nsec / NSEC_PER_USEC;
9268c2ecf20Sopenharmony_ci	}
9278c2ecf20Sopenharmony_ci
9288c2ecf20Sopenharmony_ci	drm_send_event_locked(dev, eaction->event);
9298c2ecf20Sopenharmony_ci	eaction->event = NULL;
9308c2ecf20Sopenharmony_ci	spin_unlock_irq(&dev->event_lock);
9318c2ecf20Sopenharmony_ci}
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_ci/**
9348c2ecf20Sopenharmony_ci * vmw_event_fence_action_cleanup
9358c2ecf20Sopenharmony_ci *
9368c2ecf20Sopenharmony_ci * @action: The struct vmw_fence_action embedded in a struct
9378c2ecf20Sopenharmony_ci * vmw_event_fence_action.
9388c2ecf20Sopenharmony_ci *
9398c2ecf20Sopenharmony_ci * This function is the struct vmw_fence_action destructor. It's typically
9408c2ecf20Sopenharmony_ci * called from a workqueue.
9418c2ecf20Sopenharmony_ci */
9428c2ecf20Sopenharmony_cistatic void vmw_event_fence_action_cleanup(struct vmw_fence_action *action)
9438c2ecf20Sopenharmony_ci{
9448c2ecf20Sopenharmony_ci	struct vmw_event_fence_action *eaction =
9458c2ecf20Sopenharmony_ci		container_of(action, struct vmw_event_fence_action, action);
9468c2ecf20Sopenharmony_ci
9478c2ecf20Sopenharmony_ci	vmw_fence_obj_unreference(&eaction->fence);
9488c2ecf20Sopenharmony_ci	kfree(eaction);
9498c2ecf20Sopenharmony_ci}
9508c2ecf20Sopenharmony_ci
9518c2ecf20Sopenharmony_ci
9528c2ecf20Sopenharmony_ci/**
9538c2ecf20Sopenharmony_ci * vmw_fence_obj_add_action - Add an action to a fence object.
9548c2ecf20Sopenharmony_ci *
9558c2ecf20Sopenharmony_ci * @fence - The fence object.
9568c2ecf20Sopenharmony_ci * @action - The action to add.
9578c2ecf20Sopenharmony_ci *
9588c2ecf20Sopenharmony_ci * Note that the action callbacks may be executed before this function
9598c2ecf20Sopenharmony_ci * returns.
9608c2ecf20Sopenharmony_ci */
9618c2ecf20Sopenharmony_cistatic void vmw_fence_obj_add_action(struct vmw_fence_obj *fence,
9628c2ecf20Sopenharmony_ci			      struct vmw_fence_action *action)
9638c2ecf20Sopenharmony_ci{
9648c2ecf20Sopenharmony_ci	struct vmw_fence_manager *fman = fman_from_fence(fence);
9658c2ecf20Sopenharmony_ci	bool run_update = false;
9668c2ecf20Sopenharmony_ci
9678c2ecf20Sopenharmony_ci	mutex_lock(&fman->goal_irq_mutex);
9688c2ecf20Sopenharmony_ci	spin_lock(&fman->lock);
9698c2ecf20Sopenharmony_ci
9708c2ecf20Sopenharmony_ci	fman->pending_actions[action->type]++;
9718c2ecf20Sopenharmony_ci	if (dma_fence_is_signaled_locked(&fence->base)) {
9728c2ecf20Sopenharmony_ci		struct list_head action_list;
9738c2ecf20Sopenharmony_ci
9748c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&action_list);
9758c2ecf20Sopenharmony_ci		list_add_tail(&action->head, &action_list);
9768c2ecf20Sopenharmony_ci		vmw_fences_perform_actions(fman, &action_list);
9778c2ecf20Sopenharmony_ci	} else {
9788c2ecf20Sopenharmony_ci		list_add_tail(&action->head, &fence->seq_passed_actions);
9798c2ecf20Sopenharmony_ci
9808c2ecf20Sopenharmony_ci		/*
9818c2ecf20Sopenharmony_ci		 * This function may set fman::seqno_valid, so it must
9828c2ecf20Sopenharmony_ci		 * be run with the goal_irq_mutex held.
9838c2ecf20Sopenharmony_ci		 */
9848c2ecf20Sopenharmony_ci		run_update = vmw_fence_goal_check_locked(fence);
9858c2ecf20Sopenharmony_ci	}
9868c2ecf20Sopenharmony_ci
9878c2ecf20Sopenharmony_ci	spin_unlock(&fman->lock);
9888c2ecf20Sopenharmony_ci
9898c2ecf20Sopenharmony_ci	if (run_update) {
9908c2ecf20Sopenharmony_ci		if (!fman->goal_irq_on) {
9918c2ecf20Sopenharmony_ci			fman->goal_irq_on = true;
9928c2ecf20Sopenharmony_ci			vmw_goal_waiter_add(fman->dev_priv);
9938c2ecf20Sopenharmony_ci		}
9948c2ecf20Sopenharmony_ci		vmw_fences_update(fman);
9958c2ecf20Sopenharmony_ci	}
9968c2ecf20Sopenharmony_ci	mutex_unlock(&fman->goal_irq_mutex);
9978c2ecf20Sopenharmony_ci
9988c2ecf20Sopenharmony_ci}
9998c2ecf20Sopenharmony_ci
10008c2ecf20Sopenharmony_ci/**
10018c2ecf20Sopenharmony_ci * vmw_event_fence_action_create - Post an event for sending when a fence
10028c2ecf20Sopenharmony_ci * object seqno has passed.
10038c2ecf20Sopenharmony_ci *
10048c2ecf20Sopenharmony_ci * @file_priv: The file connection on which the event should be posted.
10058c2ecf20Sopenharmony_ci * @fence: The fence object on which to post the event.
10068c2ecf20Sopenharmony_ci * @event: Event to be posted. This event should've been alloced
10078c2ecf20Sopenharmony_ci * using k[mz]alloc, and should've been completely initialized.
10088c2ecf20Sopenharmony_ci * @interruptible: Interruptible waits if possible.
10098c2ecf20Sopenharmony_ci *
10108c2ecf20Sopenharmony_ci * As a side effect, the object pointed to by @event may have been
10118c2ecf20Sopenharmony_ci * freed when this function returns. If this function returns with
10128c2ecf20Sopenharmony_ci * an error code, the caller needs to free that object.
10138c2ecf20Sopenharmony_ci */
10148c2ecf20Sopenharmony_ci
10158c2ecf20Sopenharmony_ciint vmw_event_fence_action_queue(struct drm_file *file_priv,
10168c2ecf20Sopenharmony_ci				 struct vmw_fence_obj *fence,
10178c2ecf20Sopenharmony_ci				 struct drm_pending_event *event,
10188c2ecf20Sopenharmony_ci				 uint32_t *tv_sec,
10198c2ecf20Sopenharmony_ci				 uint32_t *tv_usec,
10208c2ecf20Sopenharmony_ci				 bool interruptible)
10218c2ecf20Sopenharmony_ci{
10228c2ecf20Sopenharmony_ci	struct vmw_event_fence_action *eaction;
10238c2ecf20Sopenharmony_ci	struct vmw_fence_manager *fman = fman_from_fence(fence);
10248c2ecf20Sopenharmony_ci
10258c2ecf20Sopenharmony_ci	eaction = kzalloc(sizeof(*eaction), GFP_KERNEL);
10268c2ecf20Sopenharmony_ci	if (unlikely(!eaction))
10278c2ecf20Sopenharmony_ci		return -ENOMEM;
10288c2ecf20Sopenharmony_ci
10298c2ecf20Sopenharmony_ci	eaction->event = event;
10308c2ecf20Sopenharmony_ci
10318c2ecf20Sopenharmony_ci	eaction->action.seq_passed = vmw_event_fence_action_seq_passed;
10328c2ecf20Sopenharmony_ci	eaction->action.cleanup = vmw_event_fence_action_cleanup;
10338c2ecf20Sopenharmony_ci	eaction->action.type = VMW_ACTION_EVENT;
10348c2ecf20Sopenharmony_ci
10358c2ecf20Sopenharmony_ci	eaction->fence = vmw_fence_obj_reference(fence);
10368c2ecf20Sopenharmony_ci	eaction->dev = fman->dev_priv->dev;
10378c2ecf20Sopenharmony_ci	eaction->tv_sec = tv_sec;
10388c2ecf20Sopenharmony_ci	eaction->tv_usec = tv_usec;
10398c2ecf20Sopenharmony_ci
10408c2ecf20Sopenharmony_ci	vmw_fence_obj_add_action(fence, &eaction->action);
10418c2ecf20Sopenharmony_ci
10428c2ecf20Sopenharmony_ci	return 0;
10438c2ecf20Sopenharmony_ci}
10448c2ecf20Sopenharmony_ci
10458c2ecf20Sopenharmony_cistruct vmw_event_fence_pending {
10468c2ecf20Sopenharmony_ci	struct drm_pending_event base;
10478c2ecf20Sopenharmony_ci	struct drm_vmw_event_fence event;
10488c2ecf20Sopenharmony_ci};
10498c2ecf20Sopenharmony_ci
10508c2ecf20Sopenharmony_cistatic int vmw_event_fence_action_create(struct drm_file *file_priv,
10518c2ecf20Sopenharmony_ci				  struct vmw_fence_obj *fence,
10528c2ecf20Sopenharmony_ci				  uint32_t flags,
10538c2ecf20Sopenharmony_ci				  uint64_t user_data,
10548c2ecf20Sopenharmony_ci				  bool interruptible)
10558c2ecf20Sopenharmony_ci{
10568c2ecf20Sopenharmony_ci	struct vmw_event_fence_pending *event;
10578c2ecf20Sopenharmony_ci	struct vmw_fence_manager *fman = fman_from_fence(fence);
10588c2ecf20Sopenharmony_ci	struct drm_device *dev = fman->dev_priv->dev;
10598c2ecf20Sopenharmony_ci	int ret;
10608c2ecf20Sopenharmony_ci
10618c2ecf20Sopenharmony_ci	event = kzalloc(sizeof(*event), GFP_KERNEL);
10628c2ecf20Sopenharmony_ci	if (unlikely(!event)) {
10638c2ecf20Sopenharmony_ci		DRM_ERROR("Failed to allocate an event.\n");
10648c2ecf20Sopenharmony_ci		ret = -ENOMEM;
10658c2ecf20Sopenharmony_ci		goto out_no_space;
10668c2ecf20Sopenharmony_ci	}
10678c2ecf20Sopenharmony_ci
10688c2ecf20Sopenharmony_ci	event->event.base.type = DRM_VMW_EVENT_FENCE_SIGNALED;
10698c2ecf20Sopenharmony_ci	event->event.base.length = sizeof(*event);
10708c2ecf20Sopenharmony_ci	event->event.user_data = user_data;
10718c2ecf20Sopenharmony_ci
10728c2ecf20Sopenharmony_ci	ret = drm_event_reserve_init(dev, file_priv, &event->base, &event->event.base);
10738c2ecf20Sopenharmony_ci
10748c2ecf20Sopenharmony_ci	if (unlikely(ret != 0)) {
10758c2ecf20Sopenharmony_ci		DRM_ERROR("Failed to allocate event space for this file.\n");
10768c2ecf20Sopenharmony_ci		kfree(event);
10778c2ecf20Sopenharmony_ci		goto out_no_space;
10788c2ecf20Sopenharmony_ci	}
10798c2ecf20Sopenharmony_ci
10808c2ecf20Sopenharmony_ci	if (flags & DRM_VMW_FE_FLAG_REQ_TIME)
10818c2ecf20Sopenharmony_ci		ret = vmw_event_fence_action_queue(file_priv, fence,
10828c2ecf20Sopenharmony_ci						   &event->base,
10838c2ecf20Sopenharmony_ci						   &event->event.tv_sec,
10848c2ecf20Sopenharmony_ci						   &event->event.tv_usec,
10858c2ecf20Sopenharmony_ci						   interruptible);
10868c2ecf20Sopenharmony_ci	else
10878c2ecf20Sopenharmony_ci		ret = vmw_event_fence_action_queue(file_priv, fence,
10888c2ecf20Sopenharmony_ci						   &event->base,
10898c2ecf20Sopenharmony_ci						   NULL,
10908c2ecf20Sopenharmony_ci						   NULL,
10918c2ecf20Sopenharmony_ci						   interruptible);
10928c2ecf20Sopenharmony_ci	if (ret != 0)
10938c2ecf20Sopenharmony_ci		goto out_no_queue;
10948c2ecf20Sopenharmony_ci
10958c2ecf20Sopenharmony_ci	return 0;
10968c2ecf20Sopenharmony_ci
10978c2ecf20Sopenharmony_ciout_no_queue:
10988c2ecf20Sopenharmony_ci	drm_event_cancel_free(dev, &event->base);
10998c2ecf20Sopenharmony_ciout_no_space:
11008c2ecf20Sopenharmony_ci	return ret;
11018c2ecf20Sopenharmony_ci}
11028c2ecf20Sopenharmony_ci
11038c2ecf20Sopenharmony_ciint vmw_fence_event_ioctl(struct drm_device *dev, void *data,
11048c2ecf20Sopenharmony_ci			  struct drm_file *file_priv)
11058c2ecf20Sopenharmony_ci{
11068c2ecf20Sopenharmony_ci	struct vmw_private *dev_priv = vmw_priv(dev);
11078c2ecf20Sopenharmony_ci	struct drm_vmw_fence_event_arg *arg =
11088c2ecf20Sopenharmony_ci		(struct drm_vmw_fence_event_arg *) data;
11098c2ecf20Sopenharmony_ci	struct vmw_fence_obj *fence = NULL;
11108c2ecf20Sopenharmony_ci	struct vmw_fpriv *vmw_fp = vmw_fpriv(file_priv);
11118c2ecf20Sopenharmony_ci	struct ttm_object_file *tfile = vmw_fp->tfile;
11128c2ecf20Sopenharmony_ci	struct drm_vmw_fence_rep __user *user_fence_rep =
11138c2ecf20Sopenharmony_ci		(struct drm_vmw_fence_rep __user *)(unsigned long)
11148c2ecf20Sopenharmony_ci		arg->fence_rep;
11158c2ecf20Sopenharmony_ci	uint32_t handle;
11168c2ecf20Sopenharmony_ci	int ret;
11178c2ecf20Sopenharmony_ci
11188c2ecf20Sopenharmony_ci	/*
11198c2ecf20Sopenharmony_ci	 * Look up an existing fence object,
11208c2ecf20Sopenharmony_ci	 * and if user-space wants a new reference,
11218c2ecf20Sopenharmony_ci	 * add one.
11228c2ecf20Sopenharmony_ci	 */
11238c2ecf20Sopenharmony_ci	if (arg->handle) {
11248c2ecf20Sopenharmony_ci		struct ttm_base_object *base =
11258c2ecf20Sopenharmony_ci			vmw_fence_obj_lookup(tfile, arg->handle);
11268c2ecf20Sopenharmony_ci
11278c2ecf20Sopenharmony_ci		if (IS_ERR(base))
11288c2ecf20Sopenharmony_ci			return PTR_ERR(base);
11298c2ecf20Sopenharmony_ci
11308c2ecf20Sopenharmony_ci		fence = &(container_of(base, struct vmw_user_fence,
11318c2ecf20Sopenharmony_ci				       base)->fence);
11328c2ecf20Sopenharmony_ci		(void) vmw_fence_obj_reference(fence);
11338c2ecf20Sopenharmony_ci
11348c2ecf20Sopenharmony_ci		if (user_fence_rep != NULL) {
11358c2ecf20Sopenharmony_ci			ret = ttm_ref_object_add(vmw_fp->tfile, base,
11368c2ecf20Sopenharmony_ci						 TTM_REF_USAGE, NULL, false);
11378c2ecf20Sopenharmony_ci			if (unlikely(ret != 0)) {
11388c2ecf20Sopenharmony_ci				DRM_ERROR("Failed to reference a fence "
11398c2ecf20Sopenharmony_ci					  "object.\n");
11408c2ecf20Sopenharmony_ci				goto out_no_ref_obj;
11418c2ecf20Sopenharmony_ci			}
11428c2ecf20Sopenharmony_ci			handle = base->handle;
11438c2ecf20Sopenharmony_ci		}
11448c2ecf20Sopenharmony_ci		ttm_base_object_unref(&base);
11458c2ecf20Sopenharmony_ci	}
11468c2ecf20Sopenharmony_ci
11478c2ecf20Sopenharmony_ci	/*
11488c2ecf20Sopenharmony_ci	 * Create a new fence object.
11498c2ecf20Sopenharmony_ci	 */
11508c2ecf20Sopenharmony_ci	if (!fence) {
11518c2ecf20Sopenharmony_ci		ret = vmw_execbuf_fence_commands(file_priv, dev_priv,
11528c2ecf20Sopenharmony_ci						 &fence,
11538c2ecf20Sopenharmony_ci						 (user_fence_rep) ?
11548c2ecf20Sopenharmony_ci						 &handle : NULL);
11558c2ecf20Sopenharmony_ci		if (unlikely(ret != 0)) {
11568c2ecf20Sopenharmony_ci			DRM_ERROR("Fence event failed to create fence.\n");
11578c2ecf20Sopenharmony_ci			return ret;
11588c2ecf20Sopenharmony_ci		}
11598c2ecf20Sopenharmony_ci	}
11608c2ecf20Sopenharmony_ci
11618c2ecf20Sopenharmony_ci	BUG_ON(fence == NULL);
11628c2ecf20Sopenharmony_ci
11638c2ecf20Sopenharmony_ci	ret = vmw_event_fence_action_create(file_priv, fence,
11648c2ecf20Sopenharmony_ci					    arg->flags,
11658c2ecf20Sopenharmony_ci					    arg->user_data,
11668c2ecf20Sopenharmony_ci					    true);
11678c2ecf20Sopenharmony_ci	if (unlikely(ret != 0)) {
11688c2ecf20Sopenharmony_ci		if (ret != -ERESTARTSYS)
11698c2ecf20Sopenharmony_ci			DRM_ERROR("Failed to attach event to fence.\n");
11708c2ecf20Sopenharmony_ci		goto out_no_create;
11718c2ecf20Sopenharmony_ci	}
11728c2ecf20Sopenharmony_ci
11738c2ecf20Sopenharmony_ci	vmw_execbuf_copy_fence_user(dev_priv, vmw_fp, 0, user_fence_rep, fence,
11748c2ecf20Sopenharmony_ci				    handle, -1);
11758c2ecf20Sopenharmony_ci	vmw_fence_obj_unreference(&fence);
11768c2ecf20Sopenharmony_ci	return 0;
11778c2ecf20Sopenharmony_ciout_no_create:
11788c2ecf20Sopenharmony_ci	if (user_fence_rep != NULL)
11798c2ecf20Sopenharmony_ci		ttm_ref_object_base_unref(tfile, handle, TTM_REF_USAGE);
11808c2ecf20Sopenharmony_ciout_no_ref_obj:
11818c2ecf20Sopenharmony_ci	vmw_fence_obj_unreference(&fence);
11828c2ecf20Sopenharmony_ci	return ret;
11838c2ecf20Sopenharmony_ci}
1184