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/engine.h> 27 28 #ifdef NOUVEAU_OBJECT_MAGIC 29 static struct list_head _objlist = LIST_HEAD_INIT(_objlist); 30 static DEFINE_SPINLOCK(_objlist_lock); 31 #endif 32 33 int 34 nouveau_object_create_(struct nouveau_object *parent, 35 struct nouveau_object *engine, 36 struct nouveau_oclass *oclass, u32 pclass, 37 int size, void **pobject) 38 { 39 struct nouveau_object *object; 40 41 object = *pobject = kzalloc(size, GFP_KERNEL); 42 if (!object) 43 return -ENOMEM; 44 45 nouveau_object_ref(parent, &object->parent); 46 nouveau_object_ref(engine, (struct nouveau_object **)&object->engine); 47 object->oclass = oclass; 48 object->oclass->handle |= pclass; 49 atomic_set(&object->refcount, 1); 50 atomic_set(&object->usecount, 0); 51 52 #ifdef NOUVEAU_OBJECT_MAGIC 53 object->_magic = NOUVEAU_OBJECT_MAGIC; 54 spin_lock(&_objlist_lock); 55 list_add(&object->list, &_objlist); 56 spin_unlock(&_objlist_lock); 57 #endif 58 return 0; 59 } 60 61 int 62 _nouveau_object_ctor(struct nouveau_object *parent, 63 struct nouveau_object *engine, 64 struct nouveau_oclass *oclass, void *data, u32 size, 65 struct nouveau_object **pobject) 66 { 67 if (size != 0) 68 return -ENOSYS; 69 return nouveau_object_create(parent, engine, oclass, 0, pobject); 70 } 71 72 void 73 nouveau_object_destroy(struct nouveau_object *object) 74 { 75 #ifdef NOUVEAU_OBJECT_MAGIC 76 spin_lock(&_objlist_lock); 77 list_del(&object->list); 78 spin_unlock(&_objlist_lock); 79 #endif 80 nouveau_object_ref(NULL, (struct nouveau_object **)&object->engine); 81 nouveau_object_ref(NULL, &object->parent); 82 kfree(object); 83 } 84 85 int 86 nouveau_object_init(struct nouveau_object *object) 87 { 88 return 0; 89 } 90 91 int 92 nouveau_object_fini(struct nouveau_object *object, bool suspend) 93 { 94 return 0; 95 } 96 97 struct nouveau_ofuncs 98 nouveau_object_ofuncs = { 99 .ctor = _nouveau_object_ctor, 100 .dtor = nouveau_object_destroy, 101 .init = nouveau_object_init, 102 .fini = nouveau_object_fini, 103 }; 104 105 int 106 nouveau_object_ctor(struct nouveau_object *parent, 107 struct nouveau_object *engine, 108 struct nouveau_oclass *oclass, void *data, u32 size, 109 struct nouveau_object **pobject) 110 { 111 struct nouveau_ofuncs *ofuncs = oclass->ofuncs; 112 struct nouveau_object *object = NULL; 113 int ret; 114 115 ret = ofuncs->ctor(parent, engine, oclass, data, size, &object); 116 *pobject = object; 117 if (ret < 0) { 118 if (ret != -ENODEV) { 119 nv_error(parent, "failed to create 0x%08x, %d\n", 120 oclass->handle, ret); 121 } 122 123 if (object) { 124 ofuncs->dtor(object); 125 *pobject = NULL; 126 } 127 128 return ret; 129 } 130 131 if (ret == 0) { 132 nv_trace(object, "created\n"); 133 atomic_set(&object->refcount, 1); 134 } 135 136 return 0; 137 } 138 139 static void 140 nouveau_object_dtor(struct nouveau_object *object) 141 { 142 nv_trace(object, "destroying\n"); 143 nv_ofuncs(object)->dtor(object); 144 } 145 146 void 147 nouveau_object_ref(struct nouveau_object *obj, struct nouveau_object **ref) 148 { 149 if (obj) { 150 atomic_inc(&obj->refcount); 151 nv_trace(obj, "inc() == %d\n", atomic_read(&obj->refcount)); 152 } 153 154 if (*ref) { 155 int dead = atomic_dec_and_test(&(*ref)->refcount); 156 nv_trace(*ref, "dec() == %d\n", atomic_read(&(*ref)->refcount)); 157 if (dead) 158 nouveau_object_dtor(*ref); 159 } 160 161 *ref = obj; 162 } 163 164 int 165 nouveau_object_inc(struct nouveau_object *object) 166 { 167 int ref = atomic_add_return(1, &object->usecount); 168 int ret; 169 170 nv_trace(object, "use(+1) == %d\n", atomic_read(&object->usecount)); 171 if (ref != 1) 172 return 0; 173 174 nv_trace(object, "initialising...\n"); 175 if (object->parent) { 176 ret = nouveau_object_inc(object->parent); 177 if (ret) { 178 nv_error(object, "parent failed, %d\n", ret); 179 goto fail_parent; 180 } 181 } 182 183 if (object->engine) { 184 mutex_lock(&nv_subdev(object->engine)->mutex); 185 ret = nouveau_object_inc(&object->engine->subdev.object); 186 mutex_unlock(&nv_subdev(object->engine)->mutex); 187 if (ret) { 188 nv_error(object, "engine failed, %d\n", ret); 189 goto fail_engine; 190 } 191 } 192 193 ret = nv_ofuncs(object)->init(object); 194 atomic_set(&object->usecount, 1); 195 if (ret) { 196 nv_error(object, "init failed, %d\n", ret); 197 goto fail_self; 198 } 199 200 nv_trace(object, "initialised\n"); 201 return 0; 202 203 fail_self: 204 if (object->engine) { 205 mutex_lock(&nv_subdev(object->engine)->mutex); 206 nouveau_object_dec(&object->engine->subdev.object, false); 207 mutex_unlock(&nv_subdev(object->engine)->mutex); 208 } 209 fail_engine: 210 if (object->parent) 211 nouveau_object_dec(object->parent, false); 212 fail_parent: 213 atomic_dec(&object->usecount); 214 return ret; 215 } 216 217 static int 218 nouveau_object_decf(struct nouveau_object *object) 219 { 220 int ret; 221 222 nv_trace(object, "stopping...\n"); 223 224 ret = nv_ofuncs(object)->fini(object, false); 225 atomic_set(&object->usecount, 0); 226 if (ret) 227 nv_warn(object, "failed fini, %d\n", ret); 228 229 if (object->engine) { 230 mutex_lock(&nv_subdev(object->engine)->mutex); 231 nouveau_object_dec(&object->engine->subdev.object, false); 232 mutex_unlock(&nv_subdev(object->engine)->mutex); 233 } 234 235 if (object->parent) 236 nouveau_object_dec(object->parent, false); 237 238 nv_trace(object, "stopped\n"); 239 return 0; 240 } 241 242 static int 243 nouveau_object_decs(struct nouveau_object *object) 244 { 245 int ret, rret; 246 247 nv_trace(object, "suspending...\n"); 248 249 ret = nv_ofuncs(object)->fini(object, true); 250 atomic_set(&object->usecount, 0); 251 if (ret) { 252 nv_error(object, "failed suspend, %d\n", ret); 253 return ret; 254 } 255 256 if (object->engine) { 257 mutex_lock(&nv_subdev(object->engine)->mutex); 258 ret = nouveau_object_dec(&object->engine->subdev.object, true); 259 mutex_unlock(&nv_subdev(object->engine)->mutex); 260 if (ret) { 261 nv_warn(object, "engine failed suspend, %d\n", ret); 262 goto fail_engine; 263 } 264 } 265 266 if (object->parent) { 267 ret = nouveau_object_dec(object->parent, true); 268 if (ret) { 269 nv_warn(object, "parent failed suspend, %d\n", ret); 270 goto fail_parent; 271 } 272 } 273 274 nv_trace(object, "suspended\n"); 275 return 0; 276 277 fail_parent: 278 if (object->engine) { 279 mutex_lock(&nv_subdev(object->engine)->mutex); 280 rret = nouveau_object_inc(&object->engine->subdev.object); 281 mutex_unlock(&nv_subdev(object->engine)->mutex); 282 if (rret) 283 nv_fatal(object, "engine failed to reinit, %d\n", rret); 284 } 285 286 fail_engine: 287 rret = nv_ofuncs(object)->init(object); 288 if (rret) 289 nv_fatal(object, "failed to reinit, %d\n", rret); 290 291 return ret; 292 } 293 294 int 295 nouveau_object_dec(struct nouveau_object *object, bool suspend) 296 { 297 int ref = atomic_add_return(-1, &object->usecount); 298 int ret; 299 300 nv_trace(object, "use(-1) == %d\n", atomic_read(&object->usecount)); 301 302 if (ref == 0) { 303 if (suspend) 304 ret = nouveau_object_decs(object); 305 else 306 ret = nouveau_object_decf(object); 307 308 if (ret) { 309 atomic_inc(&object->usecount); 310 return ret; 311 } 312 } 313 314 return 0; 315 } 316 317 void 318 nouveau_object_debug(void) 319 { 320 #ifdef NOUVEAU_OBJECT_MAGIC 321 struct nouveau_object *object; 322 if (!list_empty(&_objlist)) { 323 nv_fatal(NULL, "*******************************************\n"); 324 nv_fatal(NULL, "* AIIIII! object(s) still exist!!!\n"); 325 nv_fatal(NULL, "*******************************************\n"); 326 list_for_each_entry(object, &_objlist, list) { 327 nv_fatal(object, "%p/%p/%d/%d\n", 328 object->parent, object->engine, 329 atomic_read(&object->refcount), 330 atomic_read(&object->usecount)); 331 } 332 } 333 #endif 334 } 335