1 /* $NetBSD: nouveau_core_object.c,v 1.3 2015/10/18 14:49:24 jmcneill Exp $ */
2
3 /*
4 * Copyright 2012 Red Hat Inc.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 * OTHER DEALINGS IN THE SOFTWARE.
23 *
24 * Authors: Ben Skeggs
25 */
26
27 #include <sys/cdefs.h>
28 __KERNEL_RCSID(0, "$NetBSD: nouveau_core_object.c,v 1.3 2015/10/18 14:49:24 jmcneill Exp $");
29
30 #include <core/object.h>
31 #include <core/parent.h>
32 #include <core/namedb.h>
33 #include <core/handle.h>
34 #include <core/engine.h>
35
36 #ifdef NOUVEAU_OBJECT_MAGIC
37 static struct list_head _objlist = LIST_HEAD_INIT(_objlist);
38 #ifdef __NetBSD__
39 static spinlock_t _objlist_lock;
40 #else
41 static DEFINE_SPINLOCK(_objlist_lock);
42 #endif
43 #endif
44
45 #ifdef __NetBSD__
46 void
nouveau_objects_init(void)47 nouveau_objects_init(void)
48 {
49
50 #ifdef NOUVEAU_OBJECT_MAGIC
51 spin_lock_init(&_objlist_lock);
52 #endif
53 }
54
55 void
nouveau_objects_fini(void)56 nouveau_objects_fini(void)
57 {
58
59 #ifdef NOUVEAU_OBJECT_MAGIC
60 spin_lock_destroy(&_objlist_lock);
61 #endif
62 }
63 #endif
64
65 int
nouveau_object_create_(struct nouveau_object * parent,struct nouveau_object * engine,struct nouveau_oclass * oclass,u32 pclass,int size,void ** pobject)66 nouveau_object_create_(struct nouveau_object *parent,
67 struct nouveau_object *engine,
68 struct nouveau_oclass *oclass, u32 pclass,
69 int size, void **pobject)
70 {
71 struct nouveau_object *object;
72
73 object = *pobject = kzalloc(size, GFP_KERNEL);
74 if (!object)
75 return -ENOMEM;
76
77 nouveau_object_ref(parent, &object->parent);
78 nouveau_object_ref(engine, &object->engine);
79 object->oclass = oclass;
80 object->oclass->handle |= pclass;
81 atomic_set(&object->refcount, 1);
82 atomic_set(&object->usecount, 0);
83
84 #ifdef NOUVEAU_OBJECT_MAGIC
85 object->_magic = NOUVEAU_OBJECT_MAGIC;
86 spin_lock(&_objlist_lock);
87 list_add(&object->list, &_objlist);
88 spin_unlock(&_objlist_lock);
89 #endif
90 return 0;
91 }
92
93 static int
_nouveau_object_ctor(struct nouveau_object * parent,struct nouveau_object * engine,struct nouveau_oclass * oclass,void * data,u32 size,struct nouveau_object ** pobject)94 _nouveau_object_ctor(struct nouveau_object *parent,
95 struct nouveau_object *engine,
96 struct nouveau_oclass *oclass, void *data, u32 size,
97 struct nouveau_object **pobject)
98 {
99 struct nouveau_object *object;
100 int ret;
101
102 ret = nouveau_object_create(parent, engine, oclass, 0, &object);
103 *pobject = nv_object(object);
104 if (ret)
105 return ret;
106
107 return 0;
108 }
109
110 void
nouveau_object_destroy(struct nouveau_object * object)111 nouveau_object_destroy(struct nouveau_object *object)
112 {
113 #ifdef NOUVEAU_OBJECT_MAGIC
114 spin_lock(&_objlist_lock);
115 list_del(&object->list);
116 spin_unlock(&_objlist_lock);
117 #endif
118 nouveau_object_ref(NULL, &object->engine);
119 nouveau_object_ref(NULL, &object->parent);
120 kfree(object);
121 }
122
123 static void
_nouveau_object_dtor(struct nouveau_object * object)124 _nouveau_object_dtor(struct nouveau_object *object)
125 {
126 nouveau_object_destroy(object);
127 }
128
129 int
nouveau_object_init(struct nouveau_object * object)130 nouveau_object_init(struct nouveau_object *object)
131 {
132 return 0;
133 }
134
135 static int
_nouveau_object_init(struct nouveau_object * object)136 _nouveau_object_init(struct nouveau_object *object)
137 {
138 return nouveau_object_init(object);
139 }
140
141 int
nouveau_object_fini(struct nouveau_object * object,bool suspend)142 nouveau_object_fini(struct nouveau_object *object, bool suspend)
143 {
144 return 0;
145 }
146
147 static int
_nouveau_object_fini(struct nouveau_object * object,bool suspend)148 _nouveau_object_fini(struct nouveau_object *object, bool suspend)
149 {
150 return nouveau_object_fini(object, suspend);
151 }
152
153 struct nouveau_ofuncs
154 nouveau_object_ofuncs = {
155 .ctor = _nouveau_object_ctor,
156 .dtor = _nouveau_object_dtor,
157 .init = _nouveau_object_init,
158 .fini = _nouveau_object_fini,
159 };
160
161 int
nouveau_object_ctor(struct nouveau_object * parent,struct nouveau_object * engine,struct nouveau_oclass * oclass,void * data,u32 size,struct nouveau_object ** pobject)162 nouveau_object_ctor(struct nouveau_object *parent,
163 struct nouveau_object *engine,
164 struct nouveau_oclass *oclass, void *data, u32 size,
165 struct nouveau_object **pobject)
166 {
167 struct nouveau_ofuncs *ofuncs = oclass->ofuncs;
168 struct nouveau_object *object = NULL;
169 int ret;
170
171 ret = ofuncs->ctor(parent, engine, oclass, data, size, &object);
172 *pobject = object;
173 if (ret < 0) {
174 if (ret != -ENODEV) {
175 nv_error(parent, "failed to create 0x%08x, %d\n",
176 oclass->handle, ret);
177 }
178
179 if (object) {
180 ofuncs->dtor(object);
181 *pobject = NULL;
182 }
183
184 return ret;
185 }
186
187 if (ret == 0) {
188 nv_debug(object, "created\n");
189 atomic_set(&object->refcount, 1);
190 }
191
192 return 0;
193 }
194
195 static void
nouveau_object_dtor(struct nouveau_object * object)196 nouveau_object_dtor(struct nouveau_object *object)
197 {
198 nv_debug(object, "destroying\n");
199 nv_ofuncs(object)->dtor(object);
200 }
201
202 void
nouveau_object_ref(struct nouveau_object * obj,struct nouveau_object ** ref)203 nouveau_object_ref(struct nouveau_object *obj, struct nouveau_object **ref)
204 {
205 if (obj) {
206 atomic_inc(&obj->refcount);
207 nv_trace(obj, "inc() == %d\n", atomic_read(&obj->refcount));
208 }
209
210 if (*ref) {
211 int dead = atomic_dec_and_test(&(*ref)->refcount);
212 nv_trace(*ref, "dec() == %d\n", atomic_read(&(*ref)->refcount));
213 if (dead)
214 nouveau_object_dtor(*ref);
215 }
216
217 *ref = obj;
218 }
219
220 int
nouveau_object_new(struct nouveau_object * client,u32 _parent,u32 _handle,u16 _oclass,void * data,u32 size,struct nouveau_object ** pobject)221 nouveau_object_new(struct nouveau_object *client, u32 _parent, u32 _handle,
222 u16 _oclass, void *data, u32 size,
223 struct nouveau_object **pobject)
224 {
225 struct nouveau_object *parent = NULL;
226 struct nouveau_object *engctx = NULL;
227 struct nouveau_object *object = NULL;
228 struct nouveau_object *engine;
229 struct nouveau_oclass *oclass;
230 struct nouveau_handle *handle;
231 int ret;
232
233 /* lookup parent object and ensure it *is* a parent */
234 parent = nouveau_handle_ref(client, _parent);
235 if (!parent) {
236 nv_error(client, "parent 0x%08x not found\n", _parent);
237 return -ENOENT;
238 }
239
240 if (!nv_iclass(parent, NV_PARENT_CLASS)) {
241 nv_error(parent, "cannot have children\n");
242 ret = -EINVAL;
243 goto fail_class;
244 }
245
246 /* check that parent supports the requested subclass */
247 ret = nouveau_parent_sclass(parent, _oclass, &engine, &oclass);
248 if (ret) {
249 nv_debug(parent, "illegal class 0x%04x\n", _oclass);
250 goto fail_class;
251 }
252
253 /* make sure engine init has been completed *before* any objects
254 * it controls are created - the constructors may depend on
255 * state calculated at init (ie. default context construction)
256 */
257 if (engine) {
258 ret = nouveau_object_inc(engine);
259 if (ret)
260 goto fail_class;
261 }
262
263 /* if engine requires it, create a context object to insert
264 * between the parent and its children (eg. PGRAPH context)
265 */
266 if (engine && nv_engine(engine)->cclass) {
267 ret = nouveau_object_ctor(parent, engine,
268 nv_engine(engine)->cclass,
269 data, size, &engctx);
270 if (ret)
271 goto fail_engctx;
272 } else {
273 nouveau_object_ref(parent, &engctx);
274 }
275
276 /* finally, create new object and bind it to its handle */
277 ret = nouveau_object_ctor(engctx, engine, oclass, data, size, &object);
278 *pobject = object;
279 if (ret)
280 goto fail_ctor;
281
282 ret = nouveau_object_inc(object);
283 if (ret)
284 goto fail_init;
285
286 ret = nouveau_handle_create(parent, _parent, _handle, object, &handle);
287 if (ret)
288 goto fail_handle;
289
290 ret = nouveau_handle_init(handle);
291 if (ret)
292 nouveau_handle_destroy(handle);
293
294 fail_handle:
295 nouveau_object_dec(object, false);
296 fail_init:
297 nouveau_object_ref(NULL, &object);
298 fail_ctor:
299 nouveau_object_ref(NULL, &engctx);
300 fail_engctx:
301 if (engine)
302 nouveau_object_dec(engine, false);
303 fail_class:
304 nouveau_object_ref(NULL, &parent);
305 return ret;
306 }
307
308 int
nouveau_object_del(struct nouveau_object * client,u32 _parent,u32 _handle)309 nouveau_object_del(struct nouveau_object *client, u32 _parent, u32 _handle)
310 {
311 struct nouveau_object *parent = NULL;
312 struct nouveau_object *namedb = NULL;
313 struct nouveau_handle *handle = NULL;
314
315 parent = nouveau_handle_ref(client, _parent);
316 if (!parent)
317 return -ENOENT;
318
319 namedb = nv_pclass(parent, NV_NAMEDB_CLASS);
320 if (namedb) {
321 handle = nouveau_namedb_get(nv_namedb(namedb), _handle);
322 if (handle) {
323 nouveau_namedb_put(handle);
324 nouveau_handle_fini(handle, false);
325 nouveau_handle_destroy(handle);
326 }
327 }
328
329 nouveau_object_ref(NULL, &parent);
330 return handle ? 0 : -EINVAL;
331 }
332
333 int
nouveau_object_inc(struct nouveau_object * object)334 nouveau_object_inc(struct nouveau_object *object)
335 {
336 int ref = atomic_add_return(1, &object->usecount);
337 int ret;
338
339 nv_trace(object, "use(+1) == %d\n", atomic_read(&object->usecount));
340 if (ref != 1)
341 return 0;
342
343 nv_trace(object, "initialising...\n");
344 if (object->parent) {
345 ret = nouveau_object_inc(object->parent);
346 if (ret) {
347 nv_error(object, "parent failed, %d\n", ret);
348 goto fail_parent;
349 }
350 }
351
352 if (object->engine) {
353 mutex_lock(&nv_subdev(object->engine)->mutex);
354 ret = nouveau_object_inc(object->engine);
355 mutex_unlock(&nv_subdev(object->engine)->mutex);
356 if (ret) {
357 nv_error(object, "engine failed, %d\n", ret);
358 goto fail_engine;
359 }
360 }
361
362 ret = nv_ofuncs(object)->init(object);
363 atomic_set(&object->usecount, 1);
364 if (ret) {
365 nv_error(object, "init failed, %d\n", ret);
366 goto fail_self;
367 }
368
369 nv_debug(object, "initialised\n");
370 return 0;
371
372 fail_self:
373 if (object->engine) {
374 mutex_lock(&nv_subdev(object->engine)->mutex);
375 nouveau_object_dec(object->engine, false);
376 mutex_unlock(&nv_subdev(object->engine)->mutex);
377 }
378 fail_engine:
379 if (object->parent)
380 nouveau_object_dec(object->parent, false);
381 fail_parent:
382 atomic_dec(&object->usecount);
383 return ret;
384 }
385
386 static int
nouveau_object_decf(struct nouveau_object * object)387 nouveau_object_decf(struct nouveau_object *object)
388 {
389 int ret;
390
391 nv_trace(object, "stopping...\n");
392
393 ret = nv_ofuncs(object)->fini(object, false);
394 atomic_set(&object->usecount, 0);
395 if (ret)
396 nv_warn(object, "failed fini, %d\n", ret);
397
398 if (object->engine) {
399 mutex_lock(&nv_subdev(object->engine)->mutex);
400 nouveau_object_dec(object->engine, false);
401 mutex_unlock(&nv_subdev(object->engine)->mutex);
402 }
403
404 if (object->parent)
405 nouveau_object_dec(object->parent, false);
406
407 nv_debug(object, "stopped\n");
408 return 0;
409 }
410
411 static int
nouveau_object_decs(struct nouveau_object * object)412 nouveau_object_decs(struct nouveau_object *object)
413 {
414 int ret, rret;
415
416 nv_trace(object, "suspending...\n");
417
418 ret = nv_ofuncs(object)->fini(object, true);
419 atomic_set(&object->usecount, 0);
420 if (ret) {
421 nv_error(object, "failed suspend, %d\n", ret);
422 return ret;
423 }
424
425 if (object->engine) {
426 mutex_lock(&nv_subdev(object->engine)->mutex);
427 ret = nouveau_object_dec(object->engine, true);
428 mutex_unlock(&nv_subdev(object->engine)->mutex);
429 if (ret) {
430 nv_warn(object, "engine failed suspend, %d\n", ret);
431 goto fail_engine;
432 }
433 }
434
435 if (object->parent) {
436 ret = nouveau_object_dec(object->parent, true);
437 if (ret) {
438 nv_warn(object, "parent failed suspend, %d\n", ret);
439 goto fail_parent;
440 }
441 }
442
443 nv_debug(object, "suspended\n");
444 return 0;
445
446 fail_parent:
447 if (object->engine) {
448 mutex_lock(&nv_subdev(object->engine)->mutex);
449 rret = nouveau_object_inc(object->engine);
450 mutex_unlock(&nv_subdev(object->engine)->mutex);
451 if (rret)
452 nv_fatal(object, "engine failed to reinit, %d\n", rret);
453 }
454
455 fail_engine:
456 rret = nv_ofuncs(object)->init(object);
457 if (rret)
458 nv_fatal(object, "failed to reinit, %d\n", rret);
459
460 return ret;
461 }
462
463 int
nouveau_object_dec(struct nouveau_object * object,bool suspend)464 nouveau_object_dec(struct nouveau_object *object, bool suspend)
465 {
466 int ref = atomic_add_return(-1, &object->usecount);
467 int ret;
468
469 nv_trace(object, "use(-1) == %d\n", atomic_read(&object->usecount));
470
471 if (ref == 0) {
472 if (suspend)
473 ret = nouveau_object_decs(object);
474 else
475 ret = nouveau_object_decf(object);
476
477 if (ret) {
478 atomic_inc(&object->usecount);
479 return ret;
480 }
481 }
482
483 return 0;
484 }
485
486 void
nouveau_object_debug(void)487 nouveau_object_debug(void)
488 {
489 #ifdef NOUVEAU_OBJECT_MAGIC
490 struct nouveau_object *object;
491 if (!list_empty(&_objlist)) {
492 nv_fatal(NULL, "*******************************************\n");
493 nv_fatal(NULL, "* AIIIII! object(s) still exist!!!\n");
494 nv_fatal(NULL, "*******************************************\n");
495 list_for_each_entry(object, &_objlist, list) {
496 nv_fatal(object, "%p/%p/%d/%d\n",
497 object->parent, object->engine,
498 atomic_read(&object->refcount),
499 atomic_read(&object->usecount));
500 }
501 }
502 #endif
503 }
504