162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR MIT 262306a36Sopenharmony_ci/************************************************************************** 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright 2011-2023 VMware, Inc., Palo Alto, CA., USA 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 762306a36Sopenharmony_ci * copy of this software and associated documentation files (the 862306a36Sopenharmony_ci * "Software"), to deal in the Software without restriction, including 962306a36Sopenharmony_ci * without limitation the rights to use, copy, modify, merge, publish, 1062306a36Sopenharmony_ci * distribute, sub license, and/or sell copies of the Software, and to 1162306a36Sopenharmony_ci * permit persons to whom the Software is furnished to do so, subject to 1262306a36Sopenharmony_ci * the following conditions: 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * The above copyright notice and this permission notice (including the 1562306a36Sopenharmony_ci * next paragraph) shall be included in all copies or substantial portions 1662306a36Sopenharmony_ci * of the Software. 1762306a36Sopenharmony_ci * 1862306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1962306a36Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 2062306a36Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 2162306a36Sopenharmony_ci * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, 2262306a36Sopenharmony_ci * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 2362306a36Sopenharmony_ci * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 2462306a36Sopenharmony_ci * USE OR OTHER DEALINGS IN THE SOFTWARE. 2562306a36Sopenharmony_ci * 2662306a36Sopenharmony_ci **************************************************************************/ 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#include <linux/sched/signal.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#include "vmwgfx_drv.h" 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#define VMW_FENCE_WRAP (1 << 31) 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistruct vmw_fence_manager { 3562306a36Sopenharmony_ci int num_fence_objects; 3662306a36Sopenharmony_ci struct vmw_private *dev_priv; 3762306a36Sopenharmony_ci spinlock_t lock; 3862306a36Sopenharmony_ci struct list_head fence_list; 3962306a36Sopenharmony_ci struct work_struct work; 4062306a36Sopenharmony_ci bool fifo_down; 4162306a36Sopenharmony_ci struct list_head cleanup_list; 4262306a36Sopenharmony_ci uint32_t pending_actions[VMW_ACTION_MAX]; 4362306a36Sopenharmony_ci struct mutex goal_irq_mutex; 4462306a36Sopenharmony_ci bool goal_irq_on; /* Protected by @goal_irq_mutex */ 4562306a36Sopenharmony_ci bool seqno_valid; /* Protected by @lock, and may not be set to true 4662306a36Sopenharmony_ci without the @goal_irq_mutex held. */ 4762306a36Sopenharmony_ci u64 ctx; 4862306a36Sopenharmony_ci}; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistruct vmw_user_fence { 5162306a36Sopenharmony_ci struct ttm_base_object base; 5262306a36Sopenharmony_ci struct vmw_fence_obj fence; 5362306a36Sopenharmony_ci}; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci/** 5662306a36Sopenharmony_ci * struct vmw_event_fence_action - fence action that delivers a drm event. 5762306a36Sopenharmony_ci * 5862306a36Sopenharmony_ci * @action: A struct vmw_fence_action to hook up to a fence. 5962306a36Sopenharmony_ci * @event: A pointer to the pending event. 6062306a36Sopenharmony_ci * @fence: A referenced pointer to the fence to keep it alive while @action 6162306a36Sopenharmony_ci * hangs on it. 6262306a36Sopenharmony_ci * @dev: Pointer to a struct drm_device so we can access the event stuff. 6362306a36Sopenharmony_ci * @tv_sec: If non-null, the variable pointed to will be assigned 6462306a36Sopenharmony_ci * current time tv_sec val when the fence signals. 6562306a36Sopenharmony_ci * @tv_usec: Must be set if @tv_sec is set, and the variable pointed to will 6662306a36Sopenharmony_ci * be assigned the current time tv_usec val when the fence signals. 6762306a36Sopenharmony_ci */ 6862306a36Sopenharmony_cistruct vmw_event_fence_action { 6962306a36Sopenharmony_ci struct vmw_fence_action action; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci struct drm_pending_event *event; 7262306a36Sopenharmony_ci struct vmw_fence_obj *fence; 7362306a36Sopenharmony_ci struct drm_device *dev; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci uint32_t *tv_sec; 7662306a36Sopenharmony_ci uint32_t *tv_usec; 7762306a36Sopenharmony_ci}; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic struct vmw_fence_manager * 8062306a36Sopenharmony_cifman_from_fence(struct vmw_fence_obj *fence) 8162306a36Sopenharmony_ci{ 8262306a36Sopenharmony_ci return container_of(fence->base.lock, struct vmw_fence_manager, lock); 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cistatic u32 vmw_fence_goal_read(struct vmw_private *vmw) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci if ((vmw->capabilities2 & SVGA_CAP2_EXTRA_REGS) != 0) 8862306a36Sopenharmony_ci return vmw_read(vmw, SVGA_REG_FENCE_GOAL); 8962306a36Sopenharmony_ci else 9062306a36Sopenharmony_ci return vmw_fifo_mem_read(vmw, SVGA_FIFO_FENCE_GOAL); 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic void vmw_fence_goal_write(struct vmw_private *vmw, u32 value) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci if ((vmw->capabilities2 & SVGA_CAP2_EXTRA_REGS) != 0) 9662306a36Sopenharmony_ci vmw_write(vmw, SVGA_REG_FENCE_GOAL, value); 9762306a36Sopenharmony_ci else 9862306a36Sopenharmony_ci vmw_fifo_mem_write(vmw, SVGA_FIFO_FENCE_GOAL, value); 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci/* 10262306a36Sopenharmony_ci * Note on fencing subsystem usage of irqs: 10362306a36Sopenharmony_ci * Typically the vmw_fences_update function is called 10462306a36Sopenharmony_ci * 10562306a36Sopenharmony_ci * a) When a new fence seqno has been submitted by the fifo code. 10662306a36Sopenharmony_ci * b) On-demand when we have waiters. Sleeping waiters will switch on the 10762306a36Sopenharmony_ci * ANY_FENCE irq and call vmw_fences_update function each time an ANY_FENCE 10862306a36Sopenharmony_ci * irq is received. When the last fence waiter is gone, that IRQ is masked 10962306a36Sopenharmony_ci * away. 11062306a36Sopenharmony_ci * 11162306a36Sopenharmony_ci * In situations where there are no waiters and we don't submit any new fences, 11262306a36Sopenharmony_ci * fence objects may not be signaled. This is perfectly OK, since there are 11362306a36Sopenharmony_ci * no consumers of the signaled data, but that is NOT ok when there are fence 11462306a36Sopenharmony_ci * actions attached to a fence. The fencing subsystem then makes use of the 11562306a36Sopenharmony_ci * FENCE_GOAL irq and sets the fence goal seqno to that of the next fence 11662306a36Sopenharmony_ci * which has an action attached, and each time vmw_fences_update is called, 11762306a36Sopenharmony_ci * the subsystem makes sure the fence goal seqno is updated. 11862306a36Sopenharmony_ci * 11962306a36Sopenharmony_ci * The fence goal seqno irq is on as long as there are unsignaled fence 12062306a36Sopenharmony_ci * objects with actions attached to them. 12162306a36Sopenharmony_ci */ 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic void vmw_fence_obj_destroy(struct dma_fence *f) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci struct vmw_fence_obj *fence = 12662306a36Sopenharmony_ci container_of(f, struct vmw_fence_obj, base); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci struct vmw_fence_manager *fman = fman_from_fence(fence); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci spin_lock(&fman->lock); 13162306a36Sopenharmony_ci list_del_init(&fence->head); 13262306a36Sopenharmony_ci --fman->num_fence_objects; 13362306a36Sopenharmony_ci spin_unlock(&fman->lock); 13462306a36Sopenharmony_ci fence->destroy(fence); 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cistatic const char *vmw_fence_get_driver_name(struct dma_fence *f) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci return "vmwgfx"; 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistatic const char *vmw_fence_get_timeline_name(struct dma_fence *f) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci return "svga"; 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistatic bool vmw_fence_enable_signaling(struct dma_fence *f) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci struct vmw_fence_obj *fence = 15062306a36Sopenharmony_ci container_of(f, struct vmw_fence_obj, base); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci struct vmw_fence_manager *fman = fman_from_fence(fence); 15362306a36Sopenharmony_ci struct vmw_private *dev_priv = fman->dev_priv; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci u32 seqno = vmw_fence_read(dev_priv); 15662306a36Sopenharmony_ci if (seqno - fence->base.seqno < VMW_FENCE_WRAP) 15762306a36Sopenharmony_ci return false; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci return true; 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cistruct vmwgfx_wait_cb { 16362306a36Sopenharmony_ci struct dma_fence_cb base; 16462306a36Sopenharmony_ci struct task_struct *task; 16562306a36Sopenharmony_ci}; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistatic void 16862306a36Sopenharmony_civmwgfx_wait_cb(struct dma_fence *fence, struct dma_fence_cb *cb) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci struct vmwgfx_wait_cb *wait = 17162306a36Sopenharmony_ci container_of(cb, struct vmwgfx_wait_cb, base); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci wake_up_process(wait->task); 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic void __vmw_fences_update(struct vmw_fence_manager *fman); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic long vmw_fence_wait(struct dma_fence *f, bool intr, signed long timeout) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci struct vmw_fence_obj *fence = 18162306a36Sopenharmony_ci container_of(f, struct vmw_fence_obj, base); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci struct vmw_fence_manager *fman = fman_from_fence(fence); 18462306a36Sopenharmony_ci struct vmw_private *dev_priv = fman->dev_priv; 18562306a36Sopenharmony_ci struct vmwgfx_wait_cb cb; 18662306a36Sopenharmony_ci long ret = timeout; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci if (likely(vmw_fence_obj_signaled(fence))) 18962306a36Sopenharmony_ci return timeout; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci vmw_seqno_waiter_add(dev_priv); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci spin_lock(f->lock); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &f->flags)) 19662306a36Sopenharmony_ci goto out; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci if (intr && signal_pending(current)) { 19962306a36Sopenharmony_ci ret = -ERESTARTSYS; 20062306a36Sopenharmony_ci goto out; 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci cb.base.func = vmwgfx_wait_cb; 20462306a36Sopenharmony_ci cb.task = current; 20562306a36Sopenharmony_ci list_add(&cb.base.node, &f->cb_list); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci for (;;) { 20862306a36Sopenharmony_ci __vmw_fences_update(fman); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci /* 21162306a36Sopenharmony_ci * We can use the barrier free __set_current_state() since 21262306a36Sopenharmony_ci * DMA_FENCE_FLAG_SIGNALED_BIT + wakeup is protected by the 21362306a36Sopenharmony_ci * fence spinlock. 21462306a36Sopenharmony_ci */ 21562306a36Sopenharmony_ci if (intr) 21662306a36Sopenharmony_ci __set_current_state(TASK_INTERRUPTIBLE); 21762306a36Sopenharmony_ci else 21862306a36Sopenharmony_ci __set_current_state(TASK_UNINTERRUPTIBLE); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &f->flags)) { 22162306a36Sopenharmony_ci if (ret == 0 && timeout > 0) 22262306a36Sopenharmony_ci ret = 1; 22362306a36Sopenharmony_ci break; 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci if (intr && signal_pending(current)) { 22762306a36Sopenharmony_ci ret = -ERESTARTSYS; 22862306a36Sopenharmony_ci break; 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci if (ret == 0) 23262306a36Sopenharmony_ci break; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci spin_unlock(f->lock); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci ret = schedule_timeout(ret); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci spin_lock(f->lock); 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci __set_current_state(TASK_RUNNING); 24162306a36Sopenharmony_ci if (!list_empty(&cb.base.node)) 24262306a36Sopenharmony_ci list_del(&cb.base.node); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ciout: 24562306a36Sopenharmony_ci spin_unlock(f->lock); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci vmw_seqno_waiter_remove(dev_priv); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci return ret; 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_cistatic const struct dma_fence_ops vmw_fence_ops = { 25362306a36Sopenharmony_ci .get_driver_name = vmw_fence_get_driver_name, 25462306a36Sopenharmony_ci .get_timeline_name = vmw_fence_get_timeline_name, 25562306a36Sopenharmony_ci .enable_signaling = vmw_fence_enable_signaling, 25662306a36Sopenharmony_ci .wait = vmw_fence_wait, 25762306a36Sopenharmony_ci .release = vmw_fence_obj_destroy, 25862306a36Sopenharmony_ci}; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci/* 26262306a36Sopenharmony_ci * Execute signal actions on fences recently signaled. 26362306a36Sopenharmony_ci * This is done from a workqueue so we don't have to execute 26462306a36Sopenharmony_ci * signal actions from atomic context. 26562306a36Sopenharmony_ci */ 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_cistatic void vmw_fence_work_func(struct work_struct *work) 26862306a36Sopenharmony_ci{ 26962306a36Sopenharmony_ci struct vmw_fence_manager *fman = 27062306a36Sopenharmony_ci container_of(work, struct vmw_fence_manager, work); 27162306a36Sopenharmony_ci struct list_head list; 27262306a36Sopenharmony_ci struct vmw_fence_action *action, *next_action; 27362306a36Sopenharmony_ci bool seqno_valid; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci do { 27662306a36Sopenharmony_ci INIT_LIST_HEAD(&list); 27762306a36Sopenharmony_ci mutex_lock(&fman->goal_irq_mutex); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci spin_lock(&fman->lock); 28062306a36Sopenharmony_ci list_splice_init(&fman->cleanup_list, &list); 28162306a36Sopenharmony_ci seqno_valid = fman->seqno_valid; 28262306a36Sopenharmony_ci spin_unlock(&fman->lock); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci if (!seqno_valid && fman->goal_irq_on) { 28562306a36Sopenharmony_ci fman->goal_irq_on = false; 28662306a36Sopenharmony_ci vmw_goal_waiter_remove(fman->dev_priv); 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci mutex_unlock(&fman->goal_irq_mutex); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci if (list_empty(&list)) 29162306a36Sopenharmony_ci return; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci /* 29462306a36Sopenharmony_ci * At this point, only we should be able to manipulate the 29562306a36Sopenharmony_ci * list heads of the actions we have on the private list. 29662306a36Sopenharmony_ci * hence fman::lock not held. 29762306a36Sopenharmony_ci */ 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci list_for_each_entry_safe(action, next_action, &list, head) { 30062306a36Sopenharmony_ci list_del_init(&action->head); 30162306a36Sopenharmony_ci if (action->cleanup) 30262306a36Sopenharmony_ci action->cleanup(action); 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci } while (1); 30562306a36Sopenharmony_ci} 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_cistruct vmw_fence_manager *vmw_fence_manager_init(struct vmw_private *dev_priv) 30862306a36Sopenharmony_ci{ 30962306a36Sopenharmony_ci struct vmw_fence_manager *fman = kzalloc(sizeof(*fman), GFP_KERNEL); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci if (unlikely(!fman)) 31262306a36Sopenharmony_ci return NULL; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci fman->dev_priv = dev_priv; 31562306a36Sopenharmony_ci spin_lock_init(&fman->lock); 31662306a36Sopenharmony_ci INIT_LIST_HEAD(&fman->fence_list); 31762306a36Sopenharmony_ci INIT_LIST_HEAD(&fman->cleanup_list); 31862306a36Sopenharmony_ci INIT_WORK(&fman->work, &vmw_fence_work_func); 31962306a36Sopenharmony_ci fman->fifo_down = true; 32062306a36Sopenharmony_ci mutex_init(&fman->goal_irq_mutex); 32162306a36Sopenharmony_ci fman->ctx = dma_fence_context_alloc(1); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci return fman; 32462306a36Sopenharmony_ci} 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_civoid vmw_fence_manager_takedown(struct vmw_fence_manager *fman) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci bool lists_empty; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci (void) cancel_work_sync(&fman->work); 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci spin_lock(&fman->lock); 33362306a36Sopenharmony_ci lists_empty = list_empty(&fman->fence_list) && 33462306a36Sopenharmony_ci list_empty(&fman->cleanup_list); 33562306a36Sopenharmony_ci spin_unlock(&fman->lock); 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci BUG_ON(!lists_empty); 33862306a36Sopenharmony_ci kfree(fman); 33962306a36Sopenharmony_ci} 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_cistatic int vmw_fence_obj_init(struct vmw_fence_manager *fman, 34262306a36Sopenharmony_ci struct vmw_fence_obj *fence, u32 seqno, 34362306a36Sopenharmony_ci void (*destroy) (struct vmw_fence_obj *fence)) 34462306a36Sopenharmony_ci{ 34562306a36Sopenharmony_ci int ret = 0; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci dma_fence_init(&fence->base, &vmw_fence_ops, &fman->lock, 34862306a36Sopenharmony_ci fman->ctx, seqno); 34962306a36Sopenharmony_ci INIT_LIST_HEAD(&fence->seq_passed_actions); 35062306a36Sopenharmony_ci fence->destroy = destroy; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci spin_lock(&fman->lock); 35362306a36Sopenharmony_ci if (unlikely(fman->fifo_down)) { 35462306a36Sopenharmony_ci ret = -EBUSY; 35562306a36Sopenharmony_ci goto out_unlock; 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci list_add_tail(&fence->head, &fman->fence_list); 35862306a36Sopenharmony_ci ++fman->num_fence_objects; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ciout_unlock: 36162306a36Sopenharmony_ci spin_unlock(&fman->lock); 36262306a36Sopenharmony_ci return ret; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci} 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_cistatic void vmw_fences_perform_actions(struct vmw_fence_manager *fman, 36762306a36Sopenharmony_ci struct list_head *list) 36862306a36Sopenharmony_ci{ 36962306a36Sopenharmony_ci struct vmw_fence_action *action, *next_action; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci list_for_each_entry_safe(action, next_action, list, head) { 37262306a36Sopenharmony_ci list_del_init(&action->head); 37362306a36Sopenharmony_ci fman->pending_actions[action->type]--; 37462306a36Sopenharmony_ci if (action->seq_passed != NULL) 37562306a36Sopenharmony_ci action->seq_passed(action); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci /* 37862306a36Sopenharmony_ci * Add the cleanup action to the cleanup list so that 37962306a36Sopenharmony_ci * it will be performed by a worker task. 38062306a36Sopenharmony_ci */ 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci list_add_tail(&action->head, &fman->cleanup_list); 38362306a36Sopenharmony_ci } 38462306a36Sopenharmony_ci} 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci/** 38762306a36Sopenharmony_ci * vmw_fence_goal_new_locked - Figure out a new device fence goal 38862306a36Sopenharmony_ci * seqno if needed. 38962306a36Sopenharmony_ci * 39062306a36Sopenharmony_ci * @fman: Pointer to a fence manager. 39162306a36Sopenharmony_ci * @passed_seqno: The seqno the device currently signals as passed. 39262306a36Sopenharmony_ci * 39362306a36Sopenharmony_ci * This function should be called with the fence manager lock held. 39462306a36Sopenharmony_ci * It is typically called when we have a new passed_seqno, and 39562306a36Sopenharmony_ci * we might need to update the fence goal. It checks to see whether 39662306a36Sopenharmony_ci * the current fence goal has already passed, and, in that case, 39762306a36Sopenharmony_ci * scans through all unsignaled fences to get the next fence object with an 39862306a36Sopenharmony_ci * action attached, and sets the seqno of that fence as a new fence goal. 39962306a36Sopenharmony_ci * 40062306a36Sopenharmony_ci * returns true if the device goal seqno was updated. False otherwise. 40162306a36Sopenharmony_ci */ 40262306a36Sopenharmony_cistatic bool vmw_fence_goal_new_locked(struct vmw_fence_manager *fman, 40362306a36Sopenharmony_ci u32 passed_seqno) 40462306a36Sopenharmony_ci{ 40562306a36Sopenharmony_ci u32 goal_seqno; 40662306a36Sopenharmony_ci struct vmw_fence_obj *fence; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci if (likely(!fman->seqno_valid)) 40962306a36Sopenharmony_ci return false; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci goal_seqno = vmw_fence_goal_read(fman->dev_priv); 41262306a36Sopenharmony_ci if (likely(passed_seqno - goal_seqno >= VMW_FENCE_WRAP)) 41362306a36Sopenharmony_ci return false; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci fman->seqno_valid = false; 41662306a36Sopenharmony_ci list_for_each_entry(fence, &fman->fence_list, head) { 41762306a36Sopenharmony_ci if (!list_empty(&fence->seq_passed_actions)) { 41862306a36Sopenharmony_ci fman->seqno_valid = true; 41962306a36Sopenharmony_ci vmw_fence_goal_write(fman->dev_priv, 42062306a36Sopenharmony_ci fence->base.seqno); 42162306a36Sopenharmony_ci break; 42262306a36Sopenharmony_ci } 42362306a36Sopenharmony_ci } 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci return true; 42662306a36Sopenharmony_ci} 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci/** 43062306a36Sopenharmony_ci * vmw_fence_goal_check_locked - Replace the device fence goal seqno if 43162306a36Sopenharmony_ci * needed. 43262306a36Sopenharmony_ci * 43362306a36Sopenharmony_ci * @fence: Pointer to a struct vmw_fence_obj the seqno of which should be 43462306a36Sopenharmony_ci * considered as a device fence goal. 43562306a36Sopenharmony_ci * 43662306a36Sopenharmony_ci * This function should be called with the fence manager lock held. 43762306a36Sopenharmony_ci * It is typically called when an action has been attached to a fence to 43862306a36Sopenharmony_ci * check whether the seqno of that fence should be used for a fence 43962306a36Sopenharmony_ci * goal interrupt. This is typically needed if the current fence goal is 44062306a36Sopenharmony_ci * invalid, or has a higher seqno than that of the current fence object. 44162306a36Sopenharmony_ci * 44262306a36Sopenharmony_ci * returns true if the device goal seqno was updated. False otherwise. 44362306a36Sopenharmony_ci */ 44462306a36Sopenharmony_cistatic bool vmw_fence_goal_check_locked(struct vmw_fence_obj *fence) 44562306a36Sopenharmony_ci{ 44662306a36Sopenharmony_ci struct vmw_fence_manager *fman = fman_from_fence(fence); 44762306a36Sopenharmony_ci u32 goal_seqno; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci if (dma_fence_is_signaled_locked(&fence->base)) 45062306a36Sopenharmony_ci return false; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci goal_seqno = vmw_fence_goal_read(fman->dev_priv); 45362306a36Sopenharmony_ci if (likely(fman->seqno_valid && 45462306a36Sopenharmony_ci goal_seqno - fence->base.seqno < VMW_FENCE_WRAP)) 45562306a36Sopenharmony_ci return false; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci vmw_fence_goal_write(fman->dev_priv, fence->base.seqno); 45862306a36Sopenharmony_ci fman->seqno_valid = true; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci return true; 46162306a36Sopenharmony_ci} 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_cistatic void __vmw_fences_update(struct vmw_fence_manager *fman) 46462306a36Sopenharmony_ci{ 46562306a36Sopenharmony_ci struct vmw_fence_obj *fence, *next_fence; 46662306a36Sopenharmony_ci struct list_head action_list; 46762306a36Sopenharmony_ci bool needs_rerun; 46862306a36Sopenharmony_ci uint32_t seqno, new_seqno; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci seqno = vmw_fence_read(fman->dev_priv); 47162306a36Sopenharmony_cirerun: 47262306a36Sopenharmony_ci list_for_each_entry_safe(fence, next_fence, &fman->fence_list, head) { 47362306a36Sopenharmony_ci if (seqno - fence->base.seqno < VMW_FENCE_WRAP) { 47462306a36Sopenharmony_ci list_del_init(&fence->head); 47562306a36Sopenharmony_ci dma_fence_signal_locked(&fence->base); 47662306a36Sopenharmony_ci INIT_LIST_HEAD(&action_list); 47762306a36Sopenharmony_ci list_splice_init(&fence->seq_passed_actions, 47862306a36Sopenharmony_ci &action_list); 47962306a36Sopenharmony_ci vmw_fences_perform_actions(fman, &action_list); 48062306a36Sopenharmony_ci } else 48162306a36Sopenharmony_ci break; 48262306a36Sopenharmony_ci } 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci /* 48562306a36Sopenharmony_ci * Rerun if the fence goal seqno was updated, and the 48662306a36Sopenharmony_ci * hardware might have raced with that update, so that 48762306a36Sopenharmony_ci * we missed a fence_goal irq. 48862306a36Sopenharmony_ci */ 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci needs_rerun = vmw_fence_goal_new_locked(fman, seqno); 49162306a36Sopenharmony_ci if (unlikely(needs_rerun)) { 49262306a36Sopenharmony_ci new_seqno = vmw_fence_read(fman->dev_priv); 49362306a36Sopenharmony_ci if (new_seqno != seqno) { 49462306a36Sopenharmony_ci seqno = new_seqno; 49562306a36Sopenharmony_ci goto rerun; 49662306a36Sopenharmony_ci } 49762306a36Sopenharmony_ci } 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci if (!list_empty(&fman->cleanup_list)) 50062306a36Sopenharmony_ci (void) schedule_work(&fman->work); 50162306a36Sopenharmony_ci} 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_civoid vmw_fences_update(struct vmw_fence_manager *fman) 50462306a36Sopenharmony_ci{ 50562306a36Sopenharmony_ci spin_lock(&fman->lock); 50662306a36Sopenharmony_ci __vmw_fences_update(fman); 50762306a36Sopenharmony_ci spin_unlock(&fman->lock); 50862306a36Sopenharmony_ci} 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_cibool vmw_fence_obj_signaled(struct vmw_fence_obj *fence) 51162306a36Sopenharmony_ci{ 51262306a36Sopenharmony_ci struct vmw_fence_manager *fman = fman_from_fence(fence); 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->base.flags)) 51562306a36Sopenharmony_ci return true; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci vmw_fences_update(fman); 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci return dma_fence_is_signaled(&fence->base); 52062306a36Sopenharmony_ci} 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ciint vmw_fence_obj_wait(struct vmw_fence_obj *fence, bool lazy, 52362306a36Sopenharmony_ci bool interruptible, unsigned long timeout) 52462306a36Sopenharmony_ci{ 52562306a36Sopenharmony_ci long ret = dma_fence_wait_timeout(&fence->base, interruptible, timeout); 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci if (likely(ret > 0)) 52862306a36Sopenharmony_ci return 0; 52962306a36Sopenharmony_ci else if (ret == 0) 53062306a36Sopenharmony_ci return -EBUSY; 53162306a36Sopenharmony_ci else 53262306a36Sopenharmony_ci return ret; 53362306a36Sopenharmony_ci} 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_cistatic void vmw_fence_destroy(struct vmw_fence_obj *fence) 53662306a36Sopenharmony_ci{ 53762306a36Sopenharmony_ci dma_fence_free(&fence->base); 53862306a36Sopenharmony_ci} 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ciint vmw_fence_create(struct vmw_fence_manager *fman, 54162306a36Sopenharmony_ci uint32_t seqno, 54262306a36Sopenharmony_ci struct vmw_fence_obj **p_fence) 54362306a36Sopenharmony_ci{ 54462306a36Sopenharmony_ci struct vmw_fence_obj *fence; 54562306a36Sopenharmony_ci int ret; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci fence = kzalloc(sizeof(*fence), GFP_KERNEL); 54862306a36Sopenharmony_ci if (unlikely(!fence)) 54962306a36Sopenharmony_ci return -ENOMEM; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci ret = vmw_fence_obj_init(fman, fence, seqno, 55262306a36Sopenharmony_ci vmw_fence_destroy); 55362306a36Sopenharmony_ci if (unlikely(ret != 0)) 55462306a36Sopenharmony_ci goto out_err_init; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci *p_fence = fence; 55762306a36Sopenharmony_ci return 0; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ciout_err_init: 56062306a36Sopenharmony_ci kfree(fence); 56162306a36Sopenharmony_ci return ret; 56262306a36Sopenharmony_ci} 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_cistatic void vmw_user_fence_destroy(struct vmw_fence_obj *fence) 56662306a36Sopenharmony_ci{ 56762306a36Sopenharmony_ci struct vmw_user_fence *ufence = 56862306a36Sopenharmony_ci container_of(fence, struct vmw_user_fence, fence); 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci ttm_base_object_kfree(ufence, base); 57162306a36Sopenharmony_ci} 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_cistatic void vmw_user_fence_base_release(struct ttm_base_object **p_base) 57462306a36Sopenharmony_ci{ 57562306a36Sopenharmony_ci struct ttm_base_object *base = *p_base; 57662306a36Sopenharmony_ci struct vmw_user_fence *ufence = 57762306a36Sopenharmony_ci container_of(base, struct vmw_user_fence, base); 57862306a36Sopenharmony_ci struct vmw_fence_obj *fence = &ufence->fence; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci *p_base = NULL; 58162306a36Sopenharmony_ci vmw_fence_obj_unreference(&fence); 58262306a36Sopenharmony_ci} 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ciint vmw_user_fence_create(struct drm_file *file_priv, 58562306a36Sopenharmony_ci struct vmw_fence_manager *fman, 58662306a36Sopenharmony_ci uint32_t seqno, 58762306a36Sopenharmony_ci struct vmw_fence_obj **p_fence, 58862306a36Sopenharmony_ci uint32_t *p_handle) 58962306a36Sopenharmony_ci{ 59062306a36Sopenharmony_ci struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; 59162306a36Sopenharmony_ci struct vmw_user_fence *ufence; 59262306a36Sopenharmony_ci struct vmw_fence_obj *tmp; 59362306a36Sopenharmony_ci int ret; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci ufence = kzalloc(sizeof(*ufence), GFP_KERNEL); 59662306a36Sopenharmony_ci if (unlikely(!ufence)) { 59762306a36Sopenharmony_ci ret = -ENOMEM; 59862306a36Sopenharmony_ci goto out_no_object; 59962306a36Sopenharmony_ci } 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci ret = vmw_fence_obj_init(fman, &ufence->fence, seqno, 60262306a36Sopenharmony_ci vmw_user_fence_destroy); 60362306a36Sopenharmony_ci if (unlikely(ret != 0)) { 60462306a36Sopenharmony_ci kfree(ufence); 60562306a36Sopenharmony_ci goto out_no_object; 60662306a36Sopenharmony_ci } 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci /* 60962306a36Sopenharmony_ci * The base object holds a reference which is freed in 61062306a36Sopenharmony_ci * vmw_user_fence_base_release. 61162306a36Sopenharmony_ci */ 61262306a36Sopenharmony_ci tmp = vmw_fence_obj_reference(&ufence->fence); 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci ret = ttm_base_object_init(tfile, &ufence->base, false, 61562306a36Sopenharmony_ci VMW_RES_FENCE, 61662306a36Sopenharmony_ci &vmw_user_fence_base_release); 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci if (unlikely(ret != 0)) { 62062306a36Sopenharmony_ci /* 62162306a36Sopenharmony_ci * Free the base object's reference 62262306a36Sopenharmony_ci */ 62362306a36Sopenharmony_ci vmw_fence_obj_unreference(&tmp); 62462306a36Sopenharmony_ci goto out_err; 62562306a36Sopenharmony_ci } 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci *p_fence = &ufence->fence; 62862306a36Sopenharmony_ci *p_handle = ufence->base.handle; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci return 0; 63162306a36Sopenharmony_ciout_err: 63262306a36Sopenharmony_ci tmp = &ufence->fence; 63362306a36Sopenharmony_ci vmw_fence_obj_unreference(&tmp); 63462306a36Sopenharmony_ciout_no_object: 63562306a36Sopenharmony_ci return ret; 63662306a36Sopenharmony_ci} 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci/* 63962306a36Sopenharmony_ci * vmw_fence_fifo_down - signal all unsignaled fence objects. 64062306a36Sopenharmony_ci */ 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_civoid vmw_fence_fifo_down(struct vmw_fence_manager *fman) 64362306a36Sopenharmony_ci{ 64462306a36Sopenharmony_ci struct list_head action_list; 64562306a36Sopenharmony_ci int ret; 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci /* 64862306a36Sopenharmony_ci * The list may be altered while we traverse it, so always 64962306a36Sopenharmony_ci * restart when we've released the fman->lock. 65062306a36Sopenharmony_ci */ 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci spin_lock(&fman->lock); 65362306a36Sopenharmony_ci fman->fifo_down = true; 65462306a36Sopenharmony_ci while (!list_empty(&fman->fence_list)) { 65562306a36Sopenharmony_ci struct vmw_fence_obj *fence = 65662306a36Sopenharmony_ci list_entry(fman->fence_list.prev, struct vmw_fence_obj, 65762306a36Sopenharmony_ci head); 65862306a36Sopenharmony_ci dma_fence_get(&fence->base); 65962306a36Sopenharmony_ci spin_unlock(&fman->lock); 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci ret = vmw_fence_obj_wait(fence, false, false, 66262306a36Sopenharmony_ci VMW_FENCE_WAIT_TIMEOUT); 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci if (unlikely(ret != 0)) { 66562306a36Sopenharmony_ci list_del_init(&fence->head); 66662306a36Sopenharmony_ci dma_fence_signal(&fence->base); 66762306a36Sopenharmony_ci INIT_LIST_HEAD(&action_list); 66862306a36Sopenharmony_ci list_splice_init(&fence->seq_passed_actions, 66962306a36Sopenharmony_ci &action_list); 67062306a36Sopenharmony_ci vmw_fences_perform_actions(fman, &action_list); 67162306a36Sopenharmony_ci } 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci BUG_ON(!list_empty(&fence->head)); 67462306a36Sopenharmony_ci dma_fence_put(&fence->base); 67562306a36Sopenharmony_ci spin_lock(&fman->lock); 67662306a36Sopenharmony_ci } 67762306a36Sopenharmony_ci spin_unlock(&fman->lock); 67862306a36Sopenharmony_ci} 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_civoid vmw_fence_fifo_up(struct vmw_fence_manager *fman) 68162306a36Sopenharmony_ci{ 68262306a36Sopenharmony_ci spin_lock(&fman->lock); 68362306a36Sopenharmony_ci fman->fifo_down = false; 68462306a36Sopenharmony_ci spin_unlock(&fman->lock); 68562306a36Sopenharmony_ci} 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci/** 68962306a36Sopenharmony_ci * vmw_fence_obj_lookup - Look up a user-space fence object 69062306a36Sopenharmony_ci * 69162306a36Sopenharmony_ci * @tfile: A struct ttm_object_file identifying the caller. 69262306a36Sopenharmony_ci * @handle: A handle identifying the fence object. 69362306a36Sopenharmony_ci * @return: A struct vmw_user_fence base ttm object on success or 69462306a36Sopenharmony_ci * an error pointer on failure. 69562306a36Sopenharmony_ci * 69662306a36Sopenharmony_ci * The fence object is looked up and type-checked. The caller needs 69762306a36Sopenharmony_ci * to have opened the fence object first, but since that happens on 69862306a36Sopenharmony_ci * creation and fence objects aren't shareable, that's not an 69962306a36Sopenharmony_ci * issue currently. 70062306a36Sopenharmony_ci */ 70162306a36Sopenharmony_cistatic struct ttm_base_object * 70262306a36Sopenharmony_civmw_fence_obj_lookup(struct ttm_object_file *tfile, u32 handle) 70362306a36Sopenharmony_ci{ 70462306a36Sopenharmony_ci struct ttm_base_object *base = ttm_base_object_lookup(tfile, handle); 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci if (!base) { 70762306a36Sopenharmony_ci pr_err("Invalid fence object handle 0x%08lx.\n", 70862306a36Sopenharmony_ci (unsigned long)handle); 70962306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 71062306a36Sopenharmony_ci } 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci if (base->refcount_release != vmw_user_fence_base_release) { 71362306a36Sopenharmony_ci pr_err("Invalid fence object handle 0x%08lx.\n", 71462306a36Sopenharmony_ci (unsigned long)handle); 71562306a36Sopenharmony_ci ttm_base_object_unref(&base); 71662306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 71762306a36Sopenharmony_ci } 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci return base; 72062306a36Sopenharmony_ci} 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ciint vmw_fence_obj_wait_ioctl(struct drm_device *dev, void *data, 72462306a36Sopenharmony_ci struct drm_file *file_priv) 72562306a36Sopenharmony_ci{ 72662306a36Sopenharmony_ci struct drm_vmw_fence_wait_arg *arg = 72762306a36Sopenharmony_ci (struct drm_vmw_fence_wait_arg *)data; 72862306a36Sopenharmony_ci unsigned long timeout; 72962306a36Sopenharmony_ci struct ttm_base_object *base; 73062306a36Sopenharmony_ci struct vmw_fence_obj *fence; 73162306a36Sopenharmony_ci struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; 73262306a36Sopenharmony_ci int ret; 73362306a36Sopenharmony_ci uint64_t wait_timeout = ((uint64_t)arg->timeout_us * HZ); 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci /* 73662306a36Sopenharmony_ci * 64-bit division not present on 32-bit systems, so do an 73762306a36Sopenharmony_ci * approximation. (Divide by 1000000). 73862306a36Sopenharmony_ci */ 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci wait_timeout = (wait_timeout >> 20) + (wait_timeout >> 24) - 74162306a36Sopenharmony_ci (wait_timeout >> 26); 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci if (!arg->cookie_valid) { 74462306a36Sopenharmony_ci arg->cookie_valid = 1; 74562306a36Sopenharmony_ci arg->kernel_cookie = jiffies + wait_timeout; 74662306a36Sopenharmony_ci } 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci base = vmw_fence_obj_lookup(tfile, arg->handle); 74962306a36Sopenharmony_ci if (IS_ERR(base)) 75062306a36Sopenharmony_ci return PTR_ERR(base); 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci fence = &(container_of(base, struct vmw_user_fence, base)->fence); 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci timeout = jiffies; 75562306a36Sopenharmony_ci if (time_after_eq(timeout, (unsigned long)arg->kernel_cookie)) { 75662306a36Sopenharmony_ci ret = ((vmw_fence_obj_signaled(fence)) ? 75762306a36Sopenharmony_ci 0 : -EBUSY); 75862306a36Sopenharmony_ci goto out; 75962306a36Sopenharmony_ci } 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci timeout = (unsigned long)arg->kernel_cookie - timeout; 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci ret = vmw_fence_obj_wait(fence, arg->lazy, true, timeout); 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ciout: 76662306a36Sopenharmony_ci ttm_base_object_unref(&base); 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci /* 76962306a36Sopenharmony_ci * Optionally unref the fence object. 77062306a36Sopenharmony_ci */ 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci if (ret == 0 && (arg->wait_options & DRM_VMW_WAIT_OPTION_UNREF)) 77362306a36Sopenharmony_ci return ttm_ref_object_base_unref(tfile, arg->handle); 77462306a36Sopenharmony_ci return ret; 77562306a36Sopenharmony_ci} 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ciint vmw_fence_obj_signaled_ioctl(struct drm_device *dev, void *data, 77862306a36Sopenharmony_ci struct drm_file *file_priv) 77962306a36Sopenharmony_ci{ 78062306a36Sopenharmony_ci struct drm_vmw_fence_signaled_arg *arg = 78162306a36Sopenharmony_ci (struct drm_vmw_fence_signaled_arg *) data; 78262306a36Sopenharmony_ci struct ttm_base_object *base; 78362306a36Sopenharmony_ci struct vmw_fence_obj *fence; 78462306a36Sopenharmony_ci struct vmw_fence_manager *fman; 78562306a36Sopenharmony_ci struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; 78662306a36Sopenharmony_ci struct vmw_private *dev_priv = vmw_priv(dev); 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci base = vmw_fence_obj_lookup(tfile, arg->handle); 78962306a36Sopenharmony_ci if (IS_ERR(base)) 79062306a36Sopenharmony_ci return PTR_ERR(base); 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci fence = &(container_of(base, struct vmw_user_fence, base)->fence); 79362306a36Sopenharmony_ci fman = fman_from_fence(fence); 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci arg->signaled = vmw_fence_obj_signaled(fence); 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci arg->signaled_flags = arg->flags; 79862306a36Sopenharmony_ci spin_lock(&fman->lock); 79962306a36Sopenharmony_ci arg->passed_seqno = dev_priv->last_read_seqno; 80062306a36Sopenharmony_ci spin_unlock(&fman->lock); 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci ttm_base_object_unref(&base); 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci return 0; 80562306a36Sopenharmony_ci} 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ciint vmw_fence_obj_unref_ioctl(struct drm_device *dev, void *data, 80962306a36Sopenharmony_ci struct drm_file *file_priv) 81062306a36Sopenharmony_ci{ 81162306a36Sopenharmony_ci struct drm_vmw_fence_arg *arg = 81262306a36Sopenharmony_ci (struct drm_vmw_fence_arg *) data; 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci return ttm_ref_object_base_unref(vmw_fpriv(file_priv)->tfile, 81562306a36Sopenharmony_ci arg->handle); 81662306a36Sopenharmony_ci} 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci/** 81962306a36Sopenharmony_ci * vmw_event_fence_action_seq_passed 82062306a36Sopenharmony_ci * 82162306a36Sopenharmony_ci * @action: The struct vmw_fence_action embedded in a struct 82262306a36Sopenharmony_ci * vmw_event_fence_action. 82362306a36Sopenharmony_ci * 82462306a36Sopenharmony_ci * This function is called when the seqno of the fence where @action is 82562306a36Sopenharmony_ci * attached has passed. It queues the event on the submitter's event list. 82662306a36Sopenharmony_ci * This function is always called from atomic context. 82762306a36Sopenharmony_ci */ 82862306a36Sopenharmony_cistatic void vmw_event_fence_action_seq_passed(struct vmw_fence_action *action) 82962306a36Sopenharmony_ci{ 83062306a36Sopenharmony_ci struct vmw_event_fence_action *eaction = 83162306a36Sopenharmony_ci container_of(action, struct vmw_event_fence_action, action); 83262306a36Sopenharmony_ci struct drm_device *dev = eaction->dev; 83362306a36Sopenharmony_ci struct drm_pending_event *event = eaction->event; 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci if (unlikely(event == NULL)) 83662306a36Sopenharmony_ci return; 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci spin_lock_irq(&dev->event_lock); 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci if (likely(eaction->tv_sec != NULL)) { 84162306a36Sopenharmony_ci struct timespec64 ts; 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci ktime_get_ts64(&ts); 84462306a36Sopenharmony_ci /* monotonic time, so no y2038 overflow */ 84562306a36Sopenharmony_ci *eaction->tv_sec = ts.tv_sec; 84662306a36Sopenharmony_ci *eaction->tv_usec = ts.tv_nsec / NSEC_PER_USEC; 84762306a36Sopenharmony_ci } 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci drm_send_event_locked(dev, eaction->event); 85062306a36Sopenharmony_ci eaction->event = NULL; 85162306a36Sopenharmony_ci spin_unlock_irq(&dev->event_lock); 85262306a36Sopenharmony_ci} 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci/** 85562306a36Sopenharmony_ci * vmw_event_fence_action_cleanup 85662306a36Sopenharmony_ci * 85762306a36Sopenharmony_ci * @action: The struct vmw_fence_action embedded in a struct 85862306a36Sopenharmony_ci * vmw_event_fence_action. 85962306a36Sopenharmony_ci * 86062306a36Sopenharmony_ci * This function is the struct vmw_fence_action destructor. It's typically 86162306a36Sopenharmony_ci * called from a workqueue. 86262306a36Sopenharmony_ci */ 86362306a36Sopenharmony_cistatic void vmw_event_fence_action_cleanup(struct vmw_fence_action *action) 86462306a36Sopenharmony_ci{ 86562306a36Sopenharmony_ci struct vmw_event_fence_action *eaction = 86662306a36Sopenharmony_ci container_of(action, struct vmw_event_fence_action, action); 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci vmw_fence_obj_unreference(&eaction->fence); 86962306a36Sopenharmony_ci kfree(eaction); 87062306a36Sopenharmony_ci} 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci/** 87462306a36Sopenharmony_ci * vmw_fence_obj_add_action - Add an action to a fence object. 87562306a36Sopenharmony_ci * 87662306a36Sopenharmony_ci * @fence: The fence object. 87762306a36Sopenharmony_ci * @action: The action to add. 87862306a36Sopenharmony_ci * 87962306a36Sopenharmony_ci * Note that the action callbacks may be executed before this function 88062306a36Sopenharmony_ci * returns. 88162306a36Sopenharmony_ci */ 88262306a36Sopenharmony_cistatic void vmw_fence_obj_add_action(struct vmw_fence_obj *fence, 88362306a36Sopenharmony_ci struct vmw_fence_action *action) 88462306a36Sopenharmony_ci{ 88562306a36Sopenharmony_ci struct vmw_fence_manager *fman = fman_from_fence(fence); 88662306a36Sopenharmony_ci bool run_update = false; 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci mutex_lock(&fman->goal_irq_mutex); 88962306a36Sopenharmony_ci spin_lock(&fman->lock); 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci fman->pending_actions[action->type]++; 89262306a36Sopenharmony_ci if (dma_fence_is_signaled_locked(&fence->base)) { 89362306a36Sopenharmony_ci struct list_head action_list; 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci INIT_LIST_HEAD(&action_list); 89662306a36Sopenharmony_ci list_add_tail(&action->head, &action_list); 89762306a36Sopenharmony_ci vmw_fences_perform_actions(fman, &action_list); 89862306a36Sopenharmony_ci } else { 89962306a36Sopenharmony_ci list_add_tail(&action->head, &fence->seq_passed_actions); 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci /* 90262306a36Sopenharmony_ci * This function may set fman::seqno_valid, so it must 90362306a36Sopenharmony_ci * be run with the goal_irq_mutex held. 90462306a36Sopenharmony_ci */ 90562306a36Sopenharmony_ci run_update = vmw_fence_goal_check_locked(fence); 90662306a36Sopenharmony_ci } 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci spin_unlock(&fman->lock); 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci if (run_update) { 91162306a36Sopenharmony_ci if (!fman->goal_irq_on) { 91262306a36Sopenharmony_ci fman->goal_irq_on = true; 91362306a36Sopenharmony_ci vmw_goal_waiter_add(fman->dev_priv); 91462306a36Sopenharmony_ci } 91562306a36Sopenharmony_ci vmw_fences_update(fman); 91662306a36Sopenharmony_ci } 91762306a36Sopenharmony_ci mutex_unlock(&fman->goal_irq_mutex); 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci} 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci/** 92262306a36Sopenharmony_ci * vmw_event_fence_action_queue - Post an event for sending when a fence 92362306a36Sopenharmony_ci * object seqno has passed. 92462306a36Sopenharmony_ci * 92562306a36Sopenharmony_ci * @file_priv: The file connection on which the event should be posted. 92662306a36Sopenharmony_ci * @fence: The fence object on which to post the event. 92762306a36Sopenharmony_ci * @event: Event to be posted. This event should've been alloced 92862306a36Sopenharmony_ci * using k[mz]alloc, and should've been completely initialized. 92962306a36Sopenharmony_ci * @tv_sec: If non-null, the variable pointed to will be assigned 93062306a36Sopenharmony_ci * current time tv_sec val when the fence signals. 93162306a36Sopenharmony_ci * @tv_usec: Must be set if @tv_sec is set, and the variable pointed to will 93262306a36Sopenharmony_ci * be assigned the current time tv_usec val when the fence signals. 93362306a36Sopenharmony_ci * @interruptible: Interruptible waits if possible. 93462306a36Sopenharmony_ci * 93562306a36Sopenharmony_ci * As a side effect, the object pointed to by @event may have been 93662306a36Sopenharmony_ci * freed when this function returns. If this function returns with 93762306a36Sopenharmony_ci * an error code, the caller needs to free that object. 93862306a36Sopenharmony_ci */ 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ciint vmw_event_fence_action_queue(struct drm_file *file_priv, 94162306a36Sopenharmony_ci struct vmw_fence_obj *fence, 94262306a36Sopenharmony_ci struct drm_pending_event *event, 94362306a36Sopenharmony_ci uint32_t *tv_sec, 94462306a36Sopenharmony_ci uint32_t *tv_usec, 94562306a36Sopenharmony_ci bool interruptible) 94662306a36Sopenharmony_ci{ 94762306a36Sopenharmony_ci struct vmw_event_fence_action *eaction; 94862306a36Sopenharmony_ci struct vmw_fence_manager *fman = fman_from_fence(fence); 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci eaction = kzalloc(sizeof(*eaction), GFP_KERNEL); 95162306a36Sopenharmony_ci if (unlikely(!eaction)) 95262306a36Sopenharmony_ci return -ENOMEM; 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci eaction->event = event; 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci eaction->action.seq_passed = vmw_event_fence_action_seq_passed; 95762306a36Sopenharmony_ci eaction->action.cleanup = vmw_event_fence_action_cleanup; 95862306a36Sopenharmony_ci eaction->action.type = VMW_ACTION_EVENT; 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci eaction->fence = vmw_fence_obj_reference(fence); 96162306a36Sopenharmony_ci eaction->dev = &fman->dev_priv->drm; 96262306a36Sopenharmony_ci eaction->tv_sec = tv_sec; 96362306a36Sopenharmony_ci eaction->tv_usec = tv_usec; 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci vmw_fence_obj_add_action(fence, &eaction->action); 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci return 0; 96862306a36Sopenharmony_ci} 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_cistruct vmw_event_fence_pending { 97162306a36Sopenharmony_ci struct drm_pending_event base; 97262306a36Sopenharmony_ci struct drm_vmw_event_fence event; 97362306a36Sopenharmony_ci}; 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_cistatic int vmw_event_fence_action_create(struct drm_file *file_priv, 97662306a36Sopenharmony_ci struct vmw_fence_obj *fence, 97762306a36Sopenharmony_ci uint32_t flags, 97862306a36Sopenharmony_ci uint64_t user_data, 97962306a36Sopenharmony_ci bool interruptible) 98062306a36Sopenharmony_ci{ 98162306a36Sopenharmony_ci struct vmw_event_fence_pending *event; 98262306a36Sopenharmony_ci struct vmw_fence_manager *fman = fman_from_fence(fence); 98362306a36Sopenharmony_ci struct drm_device *dev = &fman->dev_priv->drm; 98462306a36Sopenharmony_ci int ret; 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci event = kzalloc(sizeof(*event), GFP_KERNEL); 98762306a36Sopenharmony_ci if (unlikely(!event)) { 98862306a36Sopenharmony_ci DRM_ERROR("Failed to allocate an event.\n"); 98962306a36Sopenharmony_ci ret = -ENOMEM; 99062306a36Sopenharmony_ci goto out_no_space; 99162306a36Sopenharmony_ci } 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci event->event.base.type = DRM_VMW_EVENT_FENCE_SIGNALED; 99462306a36Sopenharmony_ci event->event.base.length = sizeof(*event); 99562306a36Sopenharmony_ci event->event.user_data = user_data; 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci ret = drm_event_reserve_init(dev, file_priv, &event->base, &event->event.base); 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci if (unlikely(ret != 0)) { 100062306a36Sopenharmony_ci DRM_ERROR("Failed to allocate event space for this file.\n"); 100162306a36Sopenharmony_ci kfree(event); 100262306a36Sopenharmony_ci goto out_no_space; 100362306a36Sopenharmony_ci } 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci if (flags & DRM_VMW_FE_FLAG_REQ_TIME) 100662306a36Sopenharmony_ci ret = vmw_event_fence_action_queue(file_priv, fence, 100762306a36Sopenharmony_ci &event->base, 100862306a36Sopenharmony_ci &event->event.tv_sec, 100962306a36Sopenharmony_ci &event->event.tv_usec, 101062306a36Sopenharmony_ci interruptible); 101162306a36Sopenharmony_ci else 101262306a36Sopenharmony_ci ret = vmw_event_fence_action_queue(file_priv, fence, 101362306a36Sopenharmony_ci &event->base, 101462306a36Sopenharmony_ci NULL, 101562306a36Sopenharmony_ci NULL, 101662306a36Sopenharmony_ci interruptible); 101762306a36Sopenharmony_ci if (ret != 0) 101862306a36Sopenharmony_ci goto out_no_queue; 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci return 0; 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ciout_no_queue: 102362306a36Sopenharmony_ci drm_event_cancel_free(dev, &event->base); 102462306a36Sopenharmony_ciout_no_space: 102562306a36Sopenharmony_ci return ret; 102662306a36Sopenharmony_ci} 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ciint vmw_fence_event_ioctl(struct drm_device *dev, void *data, 102962306a36Sopenharmony_ci struct drm_file *file_priv) 103062306a36Sopenharmony_ci{ 103162306a36Sopenharmony_ci struct vmw_private *dev_priv = vmw_priv(dev); 103262306a36Sopenharmony_ci struct drm_vmw_fence_event_arg *arg = 103362306a36Sopenharmony_ci (struct drm_vmw_fence_event_arg *) data; 103462306a36Sopenharmony_ci struct vmw_fence_obj *fence = NULL; 103562306a36Sopenharmony_ci struct vmw_fpriv *vmw_fp = vmw_fpriv(file_priv); 103662306a36Sopenharmony_ci struct ttm_object_file *tfile = vmw_fp->tfile; 103762306a36Sopenharmony_ci struct drm_vmw_fence_rep __user *user_fence_rep = 103862306a36Sopenharmony_ci (struct drm_vmw_fence_rep __user *)(unsigned long) 103962306a36Sopenharmony_ci arg->fence_rep; 104062306a36Sopenharmony_ci uint32_t handle; 104162306a36Sopenharmony_ci int ret; 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci /* 104462306a36Sopenharmony_ci * Look up an existing fence object, 104562306a36Sopenharmony_ci * and if user-space wants a new reference, 104662306a36Sopenharmony_ci * add one. 104762306a36Sopenharmony_ci */ 104862306a36Sopenharmony_ci if (arg->handle) { 104962306a36Sopenharmony_ci struct ttm_base_object *base = 105062306a36Sopenharmony_ci vmw_fence_obj_lookup(tfile, arg->handle); 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci if (IS_ERR(base)) 105362306a36Sopenharmony_ci return PTR_ERR(base); 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci fence = &(container_of(base, struct vmw_user_fence, 105662306a36Sopenharmony_ci base)->fence); 105762306a36Sopenharmony_ci (void) vmw_fence_obj_reference(fence); 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci if (user_fence_rep != NULL) { 106062306a36Sopenharmony_ci ret = ttm_ref_object_add(vmw_fp->tfile, base, 106162306a36Sopenharmony_ci NULL, false); 106262306a36Sopenharmony_ci if (unlikely(ret != 0)) { 106362306a36Sopenharmony_ci DRM_ERROR("Failed to reference a fence " 106462306a36Sopenharmony_ci "object.\n"); 106562306a36Sopenharmony_ci goto out_no_ref_obj; 106662306a36Sopenharmony_ci } 106762306a36Sopenharmony_ci handle = base->handle; 106862306a36Sopenharmony_ci } 106962306a36Sopenharmony_ci ttm_base_object_unref(&base); 107062306a36Sopenharmony_ci } 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci /* 107362306a36Sopenharmony_ci * Create a new fence object. 107462306a36Sopenharmony_ci */ 107562306a36Sopenharmony_ci if (!fence) { 107662306a36Sopenharmony_ci ret = vmw_execbuf_fence_commands(file_priv, dev_priv, 107762306a36Sopenharmony_ci &fence, 107862306a36Sopenharmony_ci (user_fence_rep) ? 107962306a36Sopenharmony_ci &handle : NULL); 108062306a36Sopenharmony_ci if (unlikely(ret != 0)) { 108162306a36Sopenharmony_ci DRM_ERROR("Fence event failed to create fence.\n"); 108262306a36Sopenharmony_ci return ret; 108362306a36Sopenharmony_ci } 108462306a36Sopenharmony_ci } 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci BUG_ON(fence == NULL); 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ci ret = vmw_event_fence_action_create(file_priv, fence, 108962306a36Sopenharmony_ci arg->flags, 109062306a36Sopenharmony_ci arg->user_data, 109162306a36Sopenharmony_ci true); 109262306a36Sopenharmony_ci if (unlikely(ret != 0)) { 109362306a36Sopenharmony_ci if (ret != -ERESTARTSYS) 109462306a36Sopenharmony_ci DRM_ERROR("Failed to attach event to fence.\n"); 109562306a36Sopenharmony_ci goto out_no_create; 109662306a36Sopenharmony_ci } 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci vmw_execbuf_copy_fence_user(dev_priv, vmw_fp, 0, user_fence_rep, fence, 109962306a36Sopenharmony_ci handle, -1); 110062306a36Sopenharmony_ci vmw_fence_obj_unreference(&fence); 110162306a36Sopenharmony_ci return 0; 110262306a36Sopenharmony_ciout_no_create: 110362306a36Sopenharmony_ci if (user_fence_rep != NULL) 110462306a36Sopenharmony_ci ttm_ref_object_base_unref(tfile, handle); 110562306a36Sopenharmony_ciout_no_ref_obj: 110662306a36Sopenharmony_ci vmw_fence_obj_unreference(&fence); 110762306a36Sopenharmony_ci return ret; 110862306a36Sopenharmony_ci} 1109