]>
Commit | Line | Data |
---|---|---|
9274f4a9 BS |
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/parent.h> | |
27 | #include <core/namedb.h> | |
28 | #include <core/handle.h> | |
29 | #include <core/engine.h> | |
30 | ||
31 | #ifdef NOUVEAU_OBJECT_MAGIC | |
32 | static struct list_head _objlist = LIST_HEAD_INIT(_objlist); | |
33 | static DEFINE_SPINLOCK(_objlist_lock); | |
34 | #endif | |
35 | ||
36 | int | |
37 | nouveau_object_create_(struct nouveau_object *parent, | |
38 | struct nouveau_object *engine, | |
39 | struct nouveau_oclass *oclass, u32 pclass, | |
40 | int size, void **pobject) | |
41 | { | |
42 | struct nouveau_object *object; | |
43 | ||
44 | object = *pobject = kzalloc(size, GFP_KERNEL); | |
45 | if (!object) | |
46 | return -ENOMEM; | |
47 | ||
48 | nouveau_object_ref(parent, &object->parent); | |
49 | nouveau_object_ref(engine, &object->engine); | |
50 | object->oclass = oclass; | |
51 | object->oclass->handle |= pclass; | |
52 | atomic_set(&object->refcount, 1); | |
53 | atomic_set(&object->usecount, 0); | |
54 | ||
55 | #ifdef NOUVEAU_OBJECT_MAGIC | |
56 | object->_magic = NOUVEAU_OBJECT_MAGIC; | |
57 | spin_lock(&_objlist_lock); | |
58 | list_add(&object->list, &_objlist); | |
59 | spin_unlock(&_objlist_lock); | |
60 | #endif | |
61 | return 0; | |
62 | } | |
63 | ||
5b8a43ae | 64 | static int |
9274f4a9 BS |
65 | _nouveau_object_ctor(struct nouveau_object *parent, |
66 | struct nouveau_object *engine, | |
67 | struct nouveau_oclass *oclass, void *data, u32 size, | |
68 | struct nouveau_object **pobject) | |
69 | { | |
70 | struct nouveau_object *object; | |
71 | int ret; | |
72 | ||
73 | ret = nouveau_object_create(parent, engine, oclass, 0, &object); | |
74 | *pobject = nv_object(object); | |
75 | if (ret) | |
76 | return ret; | |
77 | ||
78 | return 0; | |
79 | } | |
80 | ||
81 | void | |
82 | nouveau_object_destroy(struct nouveau_object *object) | |
83 | { | |
84 | #ifdef NOUVEAU_OBJECT_MAGIC | |
85 | spin_lock(&_objlist_lock); | |
86 | list_del(&object->list); | |
87 | spin_unlock(&_objlist_lock); | |
88 | #endif | |
89 | nouveau_object_ref(NULL, &object->engine); | |
90 | nouveau_object_ref(NULL, &object->parent); | |
91 | kfree(object); | |
92 | } | |
93 | ||
5b8a43ae | 94 | static void |
9274f4a9 BS |
95 | _nouveau_object_dtor(struct nouveau_object *object) |
96 | { | |
97 | nouveau_object_destroy(object); | |
98 | } | |
99 | ||
100 | int | |
101 | nouveau_object_init(struct nouveau_object *object) | |
102 | { | |
103 | return 0; | |
104 | } | |
105 | ||
5b8a43ae | 106 | static int |
9274f4a9 BS |
107 | _nouveau_object_init(struct nouveau_object *object) |
108 | { | |
109 | return nouveau_object_init(object); | |
110 | } | |
111 | ||
112 | int | |
113 | nouveau_object_fini(struct nouveau_object *object, bool suspend) | |
114 | { | |
115 | return 0; | |
116 | } | |
117 | ||
5b8a43ae | 118 | static int |
9274f4a9 BS |
119 | _nouveau_object_fini(struct nouveau_object *object, bool suspend) |
120 | { | |
121 | return nouveau_object_fini(object, suspend); | |
122 | } | |
123 | ||
124 | struct nouveau_ofuncs | |
125 | nouveau_object_ofuncs = { | |
126 | .ctor = _nouveau_object_ctor, | |
127 | .dtor = _nouveau_object_dtor, | |
128 | .init = _nouveau_object_init, | |
129 | .fini = _nouveau_object_fini, | |
130 | }; | |
131 | ||
132 | int | |
133 | nouveau_object_ctor(struct nouveau_object *parent, | |
134 | struct nouveau_object *engine, | |
135 | struct nouveau_oclass *oclass, void *data, u32 size, | |
136 | struct nouveau_object **pobject) | |
137 | { | |
138 | struct nouveau_ofuncs *ofuncs = oclass->ofuncs; | |
139 | int ret; | |
140 | ||
141 | *pobject = NULL; | |
142 | ||
143 | ret = ofuncs->ctor(parent, engine, oclass, data, size, pobject); | |
144 | if (ret < 0) { | |
145 | if (ret != -ENODEV) { | |
146 | nv_error(parent, "failed to create 0x%08x, %d\n", | |
147 | oclass->handle, ret); | |
148 | } | |
149 | ||
150 | if (*pobject) { | |
151 | ofuncs->dtor(*pobject); | |
152 | *pobject = NULL; | |
153 | } | |
154 | ||
155 | return ret; | |
156 | } | |
157 | ||
158 | nv_debug(*pobject, "created\n"); | |
159 | return 0; | |
160 | } | |
161 | ||
162 | static void | |
163 | nouveau_object_dtor(struct nouveau_object *object) | |
164 | { | |
165 | nv_debug(object, "destroying\n"); | |
166 | nv_ofuncs(object)->dtor(object); | |
167 | } | |
168 | ||
169 | void | |
170 | nouveau_object_ref(struct nouveau_object *obj, struct nouveau_object **ref) | |
171 | { | |
172 | if (obj) { | |
173 | atomic_inc(&obj->refcount); | |
174 | nv_trace(obj, "inc() == %d\n", atomic_read(&obj->refcount)); | |
175 | } | |
176 | ||
177 | if (*ref) { | |
178 | int dead = atomic_dec_and_test(&(*ref)->refcount); | |
179 | nv_trace(*ref, "dec() == %d\n", atomic_read(&(*ref)->refcount)); | |
180 | if (dead) | |
181 | nouveau_object_dtor(*ref); | |
182 | } | |
183 | ||
184 | *ref = obj; | |
185 | } | |
186 | ||
187 | int | |
188 | nouveau_object_new(struct nouveau_object *client, u32 _parent, u32 _handle, | |
189 | u16 _oclass, void *data, u32 size, | |
190 | struct nouveau_object **pobject) | |
191 | { | |
192 | struct nouveau_object *parent = NULL; | |
193 | struct nouveau_object *engctx = NULL; | |
194 | struct nouveau_object *object = NULL; | |
195 | struct nouveau_object *engine; | |
196 | struct nouveau_oclass *oclass; | |
197 | struct nouveau_handle *handle; | |
198 | int ret; | |
199 | ||
200 | /* lookup parent object and ensure it *is* a parent */ | |
201 | parent = nouveau_handle_ref(client, _parent); | |
202 | if (!parent) { | |
203 | nv_error(client, "parent 0x%08x not found\n", _parent); | |
204 | return -ENOENT; | |
205 | } | |
206 | ||
207 | if (!nv_iclass(parent, NV_PARENT_CLASS)) { | |
208 | nv_error(parent, "cannot have children\n"); | |
209 | ret = -EINVAL; | |
210 | goto fail_class; | |
211 | } | |
212 | ||
213 | /* check that parent supports the requested subclass */ | |
214 | ret = nouveau_parent_sclass(parent, _oclass, &engine, &oclass); | |
215 | if (ret) { | |
216 | nv_debug(parent, "illegal class 0x%04x\n", _oclass); | |
217 | goto fail_class; | |
218 | } | |
219 | ||
220 | /* make sure engine init has been completed *before* any objects | |
221 | * it controls are created - the constructors may depend on | |
222 | * state calculated at init (ie. default context construction) | |
223 | */ | |
224 | if (engine) { | |
225 | ret = nouveau_object_inc(engine); | |
226 | if (ret) | |
227 | goto fail_class; | |
228 | } | |
229 | ||
230 | /* if engine requires it, create a context object to insert | |
231 | * between the parent and its children (eg. PGRAPH context) | |
232 | */ | |
233 | if (engine && nv_engine(engine)->cclass) { | |
234 | ret = nouveau_object_ctor(parent, engine, | |
235 | nv_engine(engine)->cclass, | |
236 | data, size, &engctx); | |
237 | if (ret) | |
238 | goto fail_engctx; | |
239 | } else { | |
240 | nouveau_object_ref(parent, &engctx); | |
241 | } | |
242 | ||
243 | /* finally, create new object and bind it to its handle */ | |
244 | ret = nouveau_object_ctor(engctx, engine, oclass, data, size, &object); | |
245 | *pobject = object; | |
246 | if (ret) | |
247 | goto fail_ctor; | |
248 | ||
249 | ret = nouveau_object_inc(object); | |
250 | if (ret) | |
251 | goto fail_init; | |
252 | ||
253 | ret = nouveau_handle_create(parent, _parent, _handle, object, &handle); | |
254 | if (ret) | |
255 | goto fail_handle; | |
256 | ||
257 | ret = nouveau_handle_init(handle); | |
258 | if (ret) | |
259 | nouveau_handle_destroy(handle); | |
260 | ||
261 | fail_handle: | |
262 | nouveau_object_dec(object, false); | |
263 | fail_init: | |
264 | nouveau_object_ref(NULL, &object); | |
265 | fail_ctor: | |
266 | nouveau_object_ref(NULL, &engctx); | |
267 | fail_engctx: | |
268 | if (engine) | |
269 | nouveau_object_dec(engine, false); | |
270 | fail_class: | |
271 | nouveau_object_ref(NULL, &parent); | |
272 | return ret; | |
273 | } | |
274 | ||
275 | int | |
276 | nouveau_object_del(struct nouveau_object *client, u32 _parent, u32 _handle) | |
277 | { | |
278 | struct nouveau_object *parent = NULL; | |
279 | struct nouveau_object *namedb = NULL; | |
280 | struct nouveau_handle *handle = NULL; | |
9274f4a9 BS |
281 | |
282 | parent = nouveau_handle_ref(client, _parent); | |
283 | if (!parent) | |
284 | return -ENOENT; | |
285 | ||
286 | namedb = nv_pclass(parent, NV_NAMEDB_CLASS); | |
287 | if (namedb) { | |
288 | handle = nouveau_namedb_get(nv_namedb(namedb), _handle); | |
289 | if (handle) { | |
290 | nouveau_namedb_put(handle); | |
291 | nouveau_handle_fini(handle, false); | |
292 | nouveau_handle_destroy(handle); | |
293 | } | |
294 | } | |
295 | ||
296 | nouveau_object_ref(NULL, &parent); | |
4fa13395 | 297 | return handle ? 0 : -EINVAL; |
9274f4a9 BS |
298 | } |
299 | ||
300 | int | |
301 | nouveau_object_inc(struct nouveau_object *object) | |
302 | { | |
303 | int ref = atomic_add_return(1, &object->usecount); | |
304 | int ret; | |
305 | ||
306 | nv_trace(object, "use(+1) == %d\n", atomic_read(&object->usecount)); | |
307 | if (ref != 1) | |
308 | return 0; | |
309 | ||
310 | nv_trace(object, "initialising...\n"); | |
311 | if (object->parent) { | |
312 | ret = nouveau_object_inc(object->parent); | |
313 | if (ret) { | |
314 | nv_error(object, "parent failed, %d\n", ret); | |
315 | goto fail_parent; | |
316 | } | |
317 | } | |
318 | ||
319 | if (object->engine) { | |
320 | mutex_lock(&nv_subdev(object->engine)->mutex); | |
321 | ret = nouveau_object_inc(object->engine); | |
322 | mutex_unlock(&nv_subdev(object->engine)->mutex); | |
323 | if (ret) { | |
324 | nv_error(object, "engine failed, %d\n", ret); | |
325 | goto fail_engine; | |
326 | } | |
327 | } | |
328 | ||
329 | ret = nv_ofuncs(object)->init(object); | |
330 | if (ret) { | |
331 | nv_error(object, "init failed, %d\n", ret); | |
332 | goto fail_self; | |
333 | } | |
334 | ||
335 | nv_debug(object, "initialised\n"); | |
336 | return 0; | |
337 | ||
338 | fail_self: | |
339 | if (object->engine) { | |
340 | mutex_lock(&nv_subdev(object->engine)->mutex); | |
341 | nouveau_object_dec(object->engine, false); | |
342 | mutex_unlock(&nv_subdev(object->engine)->mutex); | |
343 | } | |
344 | fail_engine: | |
345 | if (object->parent) | |
346 | nouveau_object_dec(object->parent, false); | |
347 | fail_parent: | |
348 | atomic_dec(&object->usecount); | |
349 | return ret; | |
350 | } | |
351 | ||
352 | static int | |
353 | nouveau_object_decf(struct nouveau_object *object) | |
354 | { | |
355 | int ret; | |
356 | ||
357 | nv_trace(object, "stopping...\n"); | |
358 | ||
359 | ret = nv_ofuncs(object)->fini(object, false); | |
360 | if (ret) | |
361 | nv_warn(object, "failed fini, %d\n", ret); | |
362 | ||
363 | if (object->engine) { | |
364 | mutex_lock(&nv_subdev(object->engine)->mutex); | |
365 | nouveau_object_dec(object->engine, false); | |
366 | mutex_unlock(&nv_subdev(object->engine)->mutex); | |
367 | } | |
368 | ||
369 | if (object->parent) | |
370 | nouveau_object_dec(object->parent, false); | |
371 | ||
372 | nv_debug(object, "stopped\n"); | |
373 | return 0; | |
374 | } | |
375 | ||
376 | static int | |
377 | nouveau_object_decs(struct nouveau_object *object) | |
378 | { | |
379 | int ret, rret; | |
380 | ||
381 | nv_trace(object, "suspending...\n"); | |
382 | ||
383 | ret = nv_ofuncs(object)->fini(object, true); | |
384 | if (ret) { | |
385 | nv_error(object, "failed suspend, %d\n", ret); | |
386 | return ret; | |
387 | } | |
388 | ||
389 | if (object->engine) { | |
390 | mutex_lock(&nv_subdev(object->engine)->mutex); | |
391 | ret = nouveau_object_dec(object->engine, true); | |
392 | mutex_unlock(&nv_subdev(object->engine)->mutex); | |
393 | if (ret) { | |
394 | nv_warn(object, "engine failed suspend, %d\n", ret); | |
395 | goto fail_engine; | |
396 | } | |
397 | } | |
398 | ||
399 | if (object->parent) { | |
400 | ret = nouveau_object_dec(object->parent, true); | |
401 | if (ret) { | |
402 | nv_warn(object, "parent failed suspend, %d\n", ret); | |
403 | goto fail_parent; | |
404 | } | |
405 | } | |
406 | ||
407 | nv_debug(object, "suspended\n"); | |
408 | return 0; | |
409 | ||
410 | fail_parent: | |
411 | if (object->engine) { | |
412 | mutex_lock(&nv_subdev(object->engine)->mutex); | |
413 | rret = nouveau_object_inc(object->engine); | |
414 | mutex_unlock(&nv_subdev(object->engine)->mutex); | |
415 | if (rret) | |
416 | nv_fatal(object, "engine failed to reinit, %d\n", rret); | |
417 | } | |
418 | ||
419 | fail_engine: | |
420 | rret = nv_ofuncs(object)->init(object); | |
421 | if (rret) | |
422 | nv_fatal(object, "failed to reinit, %d\n", rret); | |
423 | ||
424 | return ret; | |
425 | } | |
426 | ||
427 | int | |
428 | nouveau_object_dec(struct nouveau_object *object, bool suspend) | |
429 | { | |
430 | int ref = atomic_add_return(-1, &object->usecount); | |
431 | int ret; | |
432 | ||
433 | nv_trace(object, "use(-1) == %d\n", atomic_read(&object->usecount)); | |
434 | ||
435 | if (ref == 0) { | |
436 | if (suspend) | |
437 | ret = nouveau_object_decs(object); | |
438 | else | |
439 | ret = nouveau_object_decf(object); | |
440 | ||
441 | if (ret) { | |
442 | atomic_inc(&object->usecount); | |
443 | return ret; | |
444 | } | |
445 | } | |
446 | ||
447 | return 0; | |
448 | } | |
449 | ||
450 | void | |
451 | nouveau_object_debug(void) | |
452 | { | |
453 | #ifdef NOUVEAU_OBJECT_MAGIC | |
454 | struct nouveau_object *object; | |
455 | if (!list_empty(&_objlist)) { | |
456 | nv_fatal(NULL, "*******************************************\n"); | |
457 | nv_fatal(NULL, "* AIIIII! object(s) still exist!!!\n"); | |
458 | nv_fatal(NULL, "*******************************************\n"); | |
459 | list_for_each_entry(object, &_objlist, list) { | |
460 | nv_fatal(object, "%p/%p/%d/%d\n", | |
461 | object->parent, object->engine, | |
462 | atomic_read(&object->refcount), | |
463 | atomic_read(&object->usecount)); | |
464 | } | |
465 | } | |
466 | #endif | |
467 | } |