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