1/* 2 * Copyright 2012 Red Hat Inc. 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice shall be included in 12 * all copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 20 * OTHER DEALINGS IN THE SOFTWARE. 21 * 22 * Authors: Ben Skeggs 23 */ 24 25#include <core/object.h> 26#include <core/namedb.h> 27#include <core/handle.h> 28#include <core/client.h> 29#include <core/engctx.h> 30 31#include <subdev/vm.h> 32 33static inline int 34nouveau_engctx_exists(struct nouveau_object *parent, 35 struct nouveau_engine *engine, void **pobject) 36{ 37 struct nouveau_engctx *engctx; 38 struct nouveau_object *parctx; 39 40 list_for_each_entry(engctx, &engine->contexts, head) { 41 parctx = nv_pclass(nv_object(engctx), NV_PARENT_CLASS); 42 if (parctx == parent) { 43 atomic_inc(&nv_object(engctx)->refcount); 44 *pobject = engctx; 45 return 1; 46 } 47 } 48 49 return 0; 50} 51 52int 53nouveau_engctx_create_(struct nouveau_object *parent, 54 struct nouveau_object *engobj, 55 struct nouveau_oclass *oclass, 56 struct nouveau_object *pargpu, 57 u32 size, u32 align, u32 flags, 58 int length, void **pobject) 59{ 60 struct nouveau_client *client = nouveau_client(parent); 61 struct nouveau_engine *engine = nv_engine(engobj); 62 struct nouveau_object *engctx; 63 unsigned long save; 64 int ret; 65 66 /* check if this engine already has a context for the parent object, 67 * and reference it instead of creating a new one 68 */ 69 spin_lock_irqsave(&engine->lock, save); 70 ret = nouveau_engctx_exists(parent, engine, pobject); 71 spin_unlock_irqrestore(&engine->lock, save); 72 if (ret) 73 return ret; 74 75 /* create the new context, supports creating both raw objects and 76 * objects backed by instance memory 77 */ 78 if (size) { 79 ret = nouveau_gpuobj_create_(parent, engobj, oclass, 80 NV_ENGCTX_CLASS, 81 pargpu, size, align, flags, 82 length, pobject); 83 } else { 84 ret = nouveau_object_create_(parent, engobj, oclass, 85 NV_ENGCTX_CLASS, length, pobject); 86 } 87 88 engctx = *pobject; 89 if (ret) 90 return ret; 91 92 /* must take the lock again and re-check a context doesn't already 93 * exist (in case of a race) - the lock had to be dropped before as 94 * it's not possible to allocate the object with it held. 95 */ 96 spin_lock_irqsave(&engine->lock, save); 97 ret = nouveau_engctx_exists(parent, engine, pobject); 98 if (ret) { 99 spin_unlock_irqrestore(&engine->lock, save); 100 nouveau_object_ref(NULL, &engctx); 101 return ret; 102 } 103 104 if (client->vm) 105 atomic_inc(&client->vm->engref[nv_engidx(engobj)]); 106 list_add(&nv_engctx(engctx)->head, &engine->contexts); 107 nv_engctx(engctx)->addr = ~0ULL; 108 spin_unlock_irqrestore(&engine->lock, save); 109 return 0; 110} 111 112void 113nouveau_engctx_destroy(struct nouveau_engctx *engctx) 114{ 115 struct nouveau_object *engobj = nv_object(engctx)->engine; 116 struct nouveau_engine *engine = nv_engine(engobj); 117 struct nouveau_client *client = nouveau_client(engctx); 118 unsigned long save; 119 120 nouveau_gpuobj_unmap(&engctx->vma); 121 spin_lock_irqsave(&engine->lock, save); 122 list_del(&engctx->head); 123 spin_unlock_irqrestore(&engine->lock, save); 124 125 if (client->vm) 126 atomic_dec(&client->vm->engref[nv_engidx(engobj)]); 127 128 if (engctx->base.size) 129 nouveau_gpuobj_destroy(&engctx->base); 130 else 131 nouveau_object_destroy(&engctx->base.base); 132} 133 134int 135nouveau_engctx_init(struct nouveau_engctx *engctx) 136{ 137 struct nouveau_object *object = nv_object(engctx); 138 struct nouveau_subdev *subdev = nv_subdev(object->engine); 139 struct nouveau_object *parent; 140 struct nouveau_subdev *pardev; 141 int ret; 142 143 ret = nouveau_gpuobj_init(&engctx->base); 144 if (ret) 145 return ret; 146 147 parent = nv_pclass(object->parent, NV_PARENT_CLASS); 148 pardev = nv_subdev(parent->engine); 149 if (nv_parent(parent)->context_attach) { 150 mutex_lock(&pardev->mutex); 151 ret = nv_parent(parent)->context_attach(parent, object); 152 mutex_unlock(&pardev->mutex); 153 } 154 155 if (ret) { 156 nv_error(parent, "failed to attach %s context, %d\n", 157 subdev->name, ret); 158 return ret; 159 } 160 161 nv_debug(parent, "attached %s context\n", subdev->name); 162 return 0; 163} 164 165int 166nouveau_engctx_fini(struct nouveau_engctx *engctx, bool suspend) 167{ 168 struct nouveau_object *object = nv_object(engctx); 169 struct nouveau_subdev *subdev = nv_subdev(object->engine); 170 struct nouveau_object *parent; 171 struct nouveau_subdev *pardev; 172 int ret = 0; 173 174 parent = nv_pclass(object->parent, NV_PARENT_CLASS); 175 pardev = nv_subdev(parent->engine); 176 if (nv_parent(parent)->context_detach) { 177 mutex_lock(&pardev->mutex); 178 ret = nv_parent(parent)->context_detach(parent, suspend, object); 179 mutex_unlock(&pardev->mutex); 180 } 181 182 if (ret) { 183 nv_error(parent, "failed to detach %s context, %d\n", 184 subdev->name, ret); 185 return ret; 186 } 187 188 nv_debug(parent, "detached %s context\n", subdev->name); 189 return nouveau_gpuobj_fini(&engctx->base, suspend); 190} 191 192int 193_nouveau_engctx_ctor(struct nouveau_object *parent, 194 struct nouveau_object *engine, 195 struct nouveau_oclass *oclass, void *data, u32 size, 196 struct nouveau_object **pobject) 197{ 198 struct nouveau_engctx *engctx; 199 int ret; 200 201 ret = nouveau_engctx_create(parent, engine, oclass, NULL, 256, 256, 202 NVOBJ_FLAG_ZERO_ALLOC, &engctx); 203 *pobject = nv_object(engctx); 204 return ret; 205} 206 207void 208_nouveau_engctx_dtor(struct nouveau_object *object) 209{ 210 nouveau_engctx_destroy(nv_engctx(object)); 211} 212 213int 214_nouveau_engctx_init(struct nouveau_object *object) 215{ 216 return nouveau_engctx_init(nv_engctx(object)); 217} 218 219 220int 221_nouveau_engctx_fini(struct nouveau_object *object, bool suspend) 222{ 223 return nouveau_engctx_fini(nv_engctx(object), suspend); 224} 225 226struct nouveau_object * 227nouveau_engctx_get(struct nouveau_engine *engine, u64 addr) 228{ 229 struct nouveau_engctx *engctx; 230 unsigned long flags; 231 232 spin_lock_irqsave(&engine->lock, flags); 233 list_for_each_entry(engctx, &engine->contexts, head) { 234 if (engctx->addr == addr) { 235 engctx->save = flags; 236 return nv_object(engctx); 237 } 238 } 239 spin_unlock_irqrestore(&engine->lock, flags); 240 return NULL; 241} 242 243void 244nouveau_engctx_put(struct nouveau_object *object) 245{ 246 if (object) { 247 struct nouveau_engine *engine = nv_engine(object->engine); 248 struct nouveau_engctx *engctx = nv_engctx(object); 249 spin_unlock_irqrestore(&engine->lock, engctx->save); 250 } 251} 252