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