1 /* 2 * Copyright (C) 2007 Ben Skeggs. 3 * All Rights Reserved. 4 * 5 * Permission is hereby granted, free of charge, to any person obtaining 6 * a copy of this software and associated documentation files (the 7 * "Software"), to deal in the Software without restriction, including 8 * without limitation the rights to use, copy, modify, merge, publish, 9 * distribute, sublicense, and/or sell copies of the Software, and to 10 * permit persons to whom the Software is furnished to do so, subject to 11 * the following conditions: 12 * 13 * The above copyright notice and this permission notice (including the 14 * next paragraph) shall be included in all copies or substantial 15 * portions of the Software. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE 21 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 22 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 23 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 * 25 */ 26 27 #include "drmP.h" 28 #include "drm.h" 29 30 #include <linux/ktime.h> 31 #include <linux/hrtimer.h> 32 33 #include "nouveau_drv.h" 34 #include "nouveau_ramht.h" 35 #include "nouveau_fence.h" 36 #include "nouveau_software.h" 37 #include "nouveau_dma.h" 38 39 void 40 nouveau_fence_context_del(struct nouveau_fence_chan *fctx) 41 { 42 struct nouveau_fence *fence, *fnext; 43 spin_lock(&fctx->lock); 44 list_for_each_entry_safe(fence, fnext, &fctx->pending, head) { 45 if (fence->work) 46 fence->work(fence->priv, false); 47 fence->channel = NULL; 48 list_del(&fence->head); 49 nouveau_fence_unref(&fence); 50 } 51 spin_unlock(&fctx->lock); 52 } 53 54 void 55 nouveau_fence_context_new(struct nouveau_fence_chan *fctx) 56 { 57 INIT_LIST_HEAD(&fctx->pending); 58 spin_lock_init(&fctx->lock); 59 } 60 61 void 62 nouveau_fence_update(struct nouveau_channel *chan) 63 { 64 struct drm_device *dev = chan->dev; 65 struct nouveau_fence_priv *priv = nv_engine(dev, NVOBJ_ENGINE_FENCE); 66 struct nouveau_fence_chan *fctx = chan->engctx[NVOBJ_ENGINE_FENCE]; 67 struct nouveau_fence *fence, *fnext; 68 69 spin_lock(&fctx->lock); 70 list_for_each_entry_safe(fence, fnext, &fctx->pending, head) { 71 if (priv->read(chan) < fence->sequence) 72 break; 73 74 if (fence->work) 75 fence->work(fence->priv, true); 76 fence->channel = NULL; 77 list_del(&fence->head); 78 nouveau_fence_unref(&fence); 79 } 80 spin_unlock(&fctx->lock); 81 } 82 83 int 84 nouveau_fence_emit(struct nouveau_fence *fence, struct nouveau_channel *chan) 85 { 86 struct drm_device *dev = chan->dev; 87 struct nouveau_fence_priv *priv = nv_engine(dev, NVOBJ_ENGINE_FENCE); 88 struct nouveau_fence_chan *fctx = chan->engctx[NVOBJ_ENGINE_FENCE]; 89 int ret; 90 91 fence->channel = chan; 92 fence->timeout = jiffies + (3 * DRM_HZ); 93 fence->sequence = ++fctx->sequence; 94 95 ret = priv->emit(fence); 96 if (!ret) { 97 kref_get(&fence->kref); 98 spin_lock(&fctx->lock); 99 list_add_tail(&fence->head, &fctx->pending); 100 spin_unlock(&fctx->lock); 101 } 102 103 return ret; 104 } 105 106 bool 107 nouveau_fence_done(struct nouveau_fence *fence) 108 { 109 if (fence->channel) 110 nouveau_fence_update(fence->channel); 111 return !fence->channel; 112 } 113 114 int 115 nouveau_fence_wait(struct nouveau_fence *fence, bool lazy, bool intr) 116 { 117 unsigned long sleep_time = NSEC_PER_MSEC / 1000; 118 ktime_t t; 119 int ret = 0; 120 121 while (!nouveau_fence_done(fence)) { 122 if (fence->timeout && time_after_eq(jiffies, fence->timeout)) { 123 ret = -EBUSY; 124 break; 125 } 126 127 __set_current_state(intr ? TASK_INTERRUPTIBLE : 128 TASK_UNINTERRUPTIBLE); 129 if (lazy) { 130 t = ktime_set(0, sleep_time); 131 schedule_hrtimeout(&t, HRTIMER_MODE_REL); 132 sleep_time *= 2; 133 if (sleep_time > NSEC_PER_MSEC) 134 sleep_time = NSEC_PER_MSEC; 135 } 136 137 if (intr && signal_pending(current)) { 138 ret = -ERESTARTSYS; 139 break; 140 } 141 } 142 143 __set_current_state(TASK_RUNNING); 144 return ret; 145 } 146 147 int 148 nouveau_fence_sync(struct nouveau_fence *fence, struct nouveau_channel *chan) 149 { 150 struct drm_device *dev = chan->dev; 151 struct nouveau_fence_priv *priv = nv_engine(dev, NVOBJ_ENGINE_FENCE); 152 struct nouveau_channel *prev; 153 int ret = 0; 154 155 prev = fence ? nouveau_channel_get_unlocked(fence->channel) : NULL; 156 if (prev) { 157 if (unlikely(prev != chan && !nouveau_fence_done(fence))) { 158 ret = priv->sync(fence, prev, chan); 159 if (unlikely(ret)) 160 ret = nouveau_fence_wait(fence, true, false); 161 } 162 nouveau_channel_put_unlocked(&prev); 163 } 164 165 return ret; 166 } 167 168 static void 169 nouveau_fence_del(struct kref *kref) 170 { 171 struct nouveau_fence *fence = container_of(kref, typeof(*fence), kref); 172 kfree(fence); 173 } 174 175 void 176 nouveau_fence_unref(struct nouveau_fence **pfence) 177 { 178 if (*pfence) 179 kref_put(&(*pfence)->kref, nouveau_fence_del); 180 *pfence = NULL; 181 } 182 183 struct nouveau_fence * 184 nouveau_fence_ref(struct nouveau_fence *fence) 185 { 186 kref_get(&fence->kref); 187 return fence; 188 } 189 190 int 191 nouveau_fence_new(struct nouveau_channel *chan, struct nouveau_fence **pfence) 192 { 193 struct nouveau_fence *fence; 194 int ret = 0; 195 196 if (unlikely(!chan->engctx[NVOBJ_ENGINE_FENCE])) 197 return -ENODEV; 198 199 fence = kzalloc(sizeof(*fence), GFP_KERNEL); 200 if (!fence) 201 return -ENOMEM; 202 kref_init(&fence->kref); 203 204 if (chan) { 205 ret = nouveau_fence_emit(fence, chan); 206 if (ret) 207 nouveau_fence_unref(&fence); 208 } 209 210 *pfence = fence; 211 return ret; 212 } 213