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