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
37static struct list_head _objlist = LIST_HEAD_INIT(_objlist);
38#ifdef __NetBSD__
39static spinlock_t _objlist_lock;
40#else
41static DEFINE_SPINLOCK(_objlist_lock);
42#endif
43#endif
44
45#ifdef __NetBSD__
46void
47nouveau_objects_init(void)
48{
49
50#ifdef NOUVEAU_OBJECT_MAGIC
51 spin_lock_init(&_objlist_lock);
52#endif
53}
54
55void
56nouveau_objects_fini(void)
57{
58
59#ifdef NOUVEAU_OBJECT_MAGIC
60 spin_lock_destroy(&_objlist_lock);
61#endif
62}
63#endif
64
65int
66nouveau_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
93static int
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
110void
111nouveau_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
123static void
124_nouveau_object_dtor(struct nouveau_object *object)
125{
126 nouveau_object_destroy(object);
127}
128
129int
130nouveau_object_init(struct nouveau_object *object)
131{
132 return 0;
133}
134
135static int
136_nouveau_object_init(struct nouveau_object *object)
137{
138 return nouveau_object_init(object);
139}
140
141int
142nouveau_object_fini(struct nouveau_object *object, bool suspend)
143{
144 return 0;
145}
146
147static int
148_nouveau_object_fini(struct nouveau_object *object, bool suspend)
149{
150 return nouveau_object_fini(object, suspend);
151}
152
153struct nouveau_ofuncs
154nouveau_object_ofuncs = {
155 .ctor = _nouveau_object_ctor,
156 .dtor = _nouveau_object_dtor,
157 .init = _nouveau_object_init,
158 .fini = _nouveau_object_fini,
159};
160
161int
162nouveau_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
195static void
196nouveau_object_dtor(struct nouveau_object *object)
197{
198 nv_debug(object, "destroying\n");
199 nv_ofuncs(object)->dtor(object);
200}
201
202void
203nouveau_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
220int
221nouveau_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
294fail_handle:
295 nouveau_object_dec(object, false);
296fail_init:
297 nouveau_object_ref(NULL, &object);
298fail_ctor:
299 nouveau_object_ref(NULL, &engctx);
300fail_engctx:
301 if (engine)
302 nouveau_object_dec(engine, false);
303fail_class:
304 nouveau_object_ref(NULL, &parent);
305 return ret;
306}
307
308int
309nouveau_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
333int
334nouveau_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
372fail_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 }
378fail_engine:
379 if (object->parent)
380 nouveau_object_dec(object->parent, false);
381fail_parent:
382 atomic_dec(&object->usecount);
383 return ret;
384}
385
386static int
387nouveau_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
411static int
412nouveau_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
446fail_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
455fail_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
463int
464nouveau_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
486void
487nouveau_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