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
nouveau_object_create_(struct nouveau_object * parent,struct nouveau_object * engine,struct nouveau_oclass * oclass,u32 pclass,int size,void ** pobject)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, &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
_nouveau_object_ctor(struct nouveau_object * parent,struct nouveau_object * engine,struct nouveau_oclass * oclass,void * data,u32 size,struct nouveau_object ** pobject)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
nouveau_object_destroy(struct nouveau_object * object)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, &object->engine);
81 nouveau_object_ref(NULL, &object->parent);
82 kfree(object);
83 }
84
85 int
nouveau_object_init(struct nouveau_object * object)86 nouveau_object_init(struct nouveau_object *object)
87 {
88 return 0;
89 }
90
91 int
nouveau_object_fini(struct nouveau_object * object,bool suspend)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
nouveau_object_ctor(struct nouveau_object * parent,struct nouveau_object * engine,struct nouveau_oclass * oclass,void * data,u32 size,struct nouveau_object ** pobject)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
nouveau_object_dtor(struct nouveau_object * object)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
nouveau_object_ref(struct nouveau_object * obj,struct nouveau_object ** ref)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
nouveau_object_inc(struct nouveau_object * object)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);
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, 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
nouveau_object_decf(struct nouveau_object * object)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, 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
nouveau_object_decs(struct nouveau_object * object)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, 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);
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
nouveau_object_dec(struct nouveau_object * object,bool suspend)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
nouveau_object_debug(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