[IBAL] Fix race in UAL between CQ callbacks and CQ destruction.
[mirror/winof/.git] / core / al / al_common.c
1 /*\r
2  * Copyright (c) 2005 SilverStorm Technologies.  All rights reserved.\r
3  * Copyright (c) 1996-2003 Intel Corporation. All rights reserved. \r
4  *\r
5  * This software is available to you under the OpenIB.org BSD license\r
6  * below:\r
7  *\r
8  *     Redistribution and use in source and binary forms, with or\r
9  *     without modification, are permitted provided that the following\r
10  *     conditions are met:\r
11  *\r
12  *      - Redistributions of source code must retain the above\r
13  *        copyright notice, this list of conditions and the following\r
14  *        disclaimer.\r
15  *\r
16  *      - Redistributions in binary form must reproduce the above\r
17  *        copyright notice, this list of conditions and the following\r
18  *        disclaimer in the documentation and/or other materials\r
19  *        provided with the distribution.\r
20  *\r
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
22  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
23  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
24  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
25  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
26  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
27  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
28  * SOFTWARE.\r
29  *\r
30  * $Id$\r
31  */\r
32 \r
33 #include "al.h"\r
34 #include "al_ci_ca.h"\r
35 #include "al_common.h"\r
36 #include "al_debug.h"\r
37 #include "al_mgr.h"\r
38 #include <complib/cl_math.h>\r
39 #include "ib_common.h"\r
40 \r
41 \r
42 \r
43 #if AL_OBJ_PRIVATE_ASYNC_PROC\r
44 cl_async_proc_t         *gp_async_obj_mgr = NULL;\r
45 #endif\r
46 \r
47 \r
48 boolean_t\r
49 destroy_obj(\r
50         IN                              struct _al_obj                          *p_obj,\r
51         IN              const   ib_pfn_destroy_cb_t                     pfn_destroy_cb );\r
52 \r
53 \r
54 void\r
55 async_destroy_cb(\r
56         IN                              cl_async_proc_item_t            *p_item );\r
57 \r
58 \r
59 void\r
60 async_destroy_obj(\r
61         IN                              struct _al_obj                          *p_obj,\r
62         IN              const   ib_pfn_destroy_cb_t                     pfn_destroy_cb );\r
63 \r
64 \r
65 void\r
66 sync_destroy_obj(\r
67         IN                              struct _al_obj                          *p_obj,\r
68         IN              const   ib_pfn_destroy_cb_t                     pfn_destroy_cb );\r
69 \r
70 \r
71 #if defined( _DEBUG_ )\r
72 const char* ib_obj_type_str[] =\r
73 {\r
74         "AL_OBJ_TYPE_UNKNOWN",\r
75         "AL_OBJ_TYPE_H_AL",\r
76         "AL_OBJ_TYPE_H_QP",\r
77         "AL_OBJ_TYPE_H_AV",\r
78         "AL_OBJ_TYPE_H_MR",\r
79         "AL_OBJ_TYPE_H_MW",\r
80         "AL_OBJ_TYPE_H_PD",\r
81         "AL_OBJ_TYPE_H_CA",\r
82         "AL_OBJ_TYPE_H_CQ",\r
83         "AL_OBJ_TYPE_H_CONN",\r
84         "AL_OBJ_TYPE_H_LISTEN",\r
85         "AL_OBJ_TYPE_H_IOC",\r
86         "AL_OBJ_TYPE_H_SVC_ENTRY",\r
87         "AL_OBJ_TYPE_H_PNP",\r
88         "AL_OBJ_TYPE_H_SA_REQ",\r
89         "AL_OBJ_TYPE_H_MCAST",\r
90         "AL_OBJ_TYPE_H_ATTACH",\r
91         "AL_OBJ_TYPE_H_MAD",\r
92         "AL_OBJ_TYPE_H_MAD_POOL",\r
93         "AL_OBJ_TYPE_H_POOL_KEY",\r
94         "AL_OBJ_TYPE_H_MAD_SVC",\r
95         "AL_OBJ_TYPE_CI_CA",\r
96         "AL_OBJ_TYPE_CM",\r
97         "AL_OBJ_TYPE_SMI",\r
98         "AL_OBJ_TYPE_DM",\r
99         "AL_OBJ_TYPE_IOU",\r
100         "AL_OBJ_TYPE_LOADER",\r
101         "AL_OBJ_TYPE_MAD_POOL",\r
102         "AL_OBJ_TYPE_MAD_DISP",\r
103         "AL_OBJ_TYPE_AL_MGR",\r
104         "AL_OBJ_TYPE_PNP_MGR",\r
105         "AL_OBJ_TYPE_IOC_PNP_MGR",\r
106         "AL_OBJ_TYPE_IOC_PNP_SVC",\r
107         "AL_OBJ_TYPE_QUERY_SVC",\r
108         "AL_OBJ_TYPE_MCAST_SVC",\r
109         "AL_OBJ_TYPE_SA_REQ_SVC",\r
110         "AL_OBJ_TYPE_RES_MGR",\r
111         "AL_OBJ_TYPE_H_CA_ATTR",\r
112         "AL_OBJ_TYPE_H_PNP_EVENT",\r
113         "AL_OBJ_TYPE_H_SA_REG"\r
114 };\r
115 #endif\r
116 \r
117 \r
118 /*\r
119  * Used to force synchronous destruction of AL objects.\r
120  */\r
121 void\r
122 ib_sync_destroy(\r
123         IN                              void                                            *context )\r
124 {\r
125         UNUSED_PARAM( context );\r
126 }\r
127 \r
128 \r
129 void\r
130 construct_al_obj(\r
131         IN                              al_obj_t * const                        p_obj,\r
132         IN              const   al_obj_type_t                           obj_type )\r
133 {\r
134         CL_ASSERT( p_obj );\r
135         cl_memclr( p_obj, sizeof( al_obj_t ) );\r
136 \r
137         cl_spinlock_construct( &p_obj->lock );\r
138         p_obj->state = CL_UNINITIALIZED;\r
139         p_obj->type = obj_type;\r
140         p_obj->timeout_ms = AL_DEFAULT_TIMEOUT_MS;\r
141         p_obj->ref_cnt = 1;\r
142         cl_event_construct( &p_obj->event );\r
143 \r
144         /* Insert the object into the global tracking list. */\r
145         if( p_obj != &gp_al_mgr->obj )\r
146         {\r
147                 cl_spinlock_acquire( &gp_al_mgr->lock );\r
148                 cl_qlist_insert_tail( &gp_al_mgr->al_obj_list, &p_obj->list_item );\r
149                 cl_spinlock_release( &gp_al_mgr->lock );\r
150                 ref_al_obj( &gp_al_mgr->obj );\r
151         }\r
152 }\r
153 \r
154 \r
155 \r
156 ib_api_status_t\r
157 init_al_obj(\r
158         IN                              al_obj_t * const                        p_obj,\r
159         IN              const   void* const                                     context,\r
160         IN                              boolean_t                                       async_destroy,\r
161         IN              const   al_pfn_destroying_t                     pfn_destroying,\r
162         IN              const   al_pfn_cleanup_t                        pfn_cleanup,\r
163         IN              const   al_pfn_free_t                           pfn_free )\r
164 {\r
165         cl_status_t                             cl_status;\r
166 \r
167         CL_ENTER( AL_DBG_AL_OBJ, g_al_dbg_lvl );\r
168         CL_ASSERT( p_obj && pfn_free );\r
169         CL_ASSERT( p_obj->state == CL_UNINITIALIZED );\r
170         CL_TRACE( AL_DBG_AL_OBJ, g_al_dbg_lvl, ("%p\n", p_obj ) );\r
171 \r
172         /* Initialize the object. */\r
173         p_obj->async_item.pfn_callback = async_destroy_cb;\r
174         p_obj->pfn_free = pfn_free;\r
175 \r
176         p_obj->context = context;\r
177 \r
178         if( async_destroy && !(p_obj->type & AL_OBJ_SUBTYPE_UM_EXPORT) )\r
179                 p_obj->pfn_destroy = async_destroy_obj;\r
180         else\r
181                 p_obj->pfn_destroy = sync_destroy_obj;\r
182 \r
183         p_obj->pfn_destroying = pfn_destroying;\r
184 \r
185         p_obj->pfn_cleanup = pfn_cleanup;\r
186         p_obj->user_destroy_cb = NULL;\r
187 \r
188         cl_qlist_init( &p_obj->obj_list );\r
189         cl_status = cl_spinlock_init( &p_obj->lock );\r
190         if( cl_status != CL_SUCCESS )\r
191         {\r
192                 return ib_convert_cl_status( cl_status );\r
193         }\r
194 \r
195         cl_status = cl_event_init( &p_obj->event, FALSE );\r
196         if( cl_status != CL_SUCCESS )\r
197         {\r
198                 return ib_convert_cl_status( cl_status );\r
199         }\r
200 \r
201         p_obj->state = CL_INITIALIZED;\r
202 \r
203         /*\r
204          * Hold an extra reference on the object until creation is complete.\r
205          * This prevents a client's destruction of the object during asynchronous\r
206          * event callback processing from deallocating the object before the\r
207          * creation is complete.\r
208          */\r
209         ref_al_obj( p_obj );\r
210 \r
211         CL_EXIT( AL_DBG_AL_OBJ, g_al_dbg_lvl );\r
212         return IB_SUCCESS;\r
213 }\r
214 \r
215 \r
216 void\r
217 reset_al_obj(\r
218         IN                              al_obj_t * const                        p_obj )\r
219 {\r
220         CL_ASSERT( p_obj && (p_obj->ref_cnt == 0) );\r
221         CL_ASSERT( p_obj->state == CL_DESTROYING );\r
222 \r
223         p_obj->ref_cnt = 1;\r
224         p_obj->desc_cnt = 0;\r
225         p_obj->state = CL_INITIALIZED;\r
226         p_obj->h_al = NULL;\r
227         p_obj->hdl = AL_INVALID_HANDLE;\r
228 }\r
229 \r
230 \r
231 \r
232 void\r
233 set_al_obj_timeout(\r
234         IN                              al_obj_t * const                        p_obj,\r
235         IN              const   uint32_t                                        timeout_ms )\r
236 {\r
237         CL_ASSERT( p_obj );\r
238 \r
239         /* Only increase timeout values. */\r
240         p_obj->timeout_ms = MAX( p_obj->timeout_ms, timeout_ms );\r
241 }\r
242 \r
243 \r
244 \r
245 void\r
246 inc_al_obj_desc(\r
247         IN                              al_obj_t * const                        p_obj,\r
248         IN              const   uint32_t                                        desc_cnt )\r
249 {\r
250         CL_ASSERT( p_obj );\r
251 \r
252         /* Increment the number of descendants. */\r
253         p_obj->desc_cnt += desc_cnt;\r
254 }\r
255 \r
256 \r
257 \r
258 ib_api_status_t\r
259 attach_al_obj(\r
260         IN                              al_obj_t * const                        p_parent_obj,\r
261         IN                              al_obj_t * const                        p_child_obj )\r
262 {\r
263         AL_ENTER( AL_DBG_AL_OBJ );\r
264         \r
265         CL_ASSERT( p_child_obj->state == CL_INITIALIZED );\r
266 \r
267         AL_TRACE( AL_DBG_AL_OBJ,\r
268                 ("%p(%s) to %p(%s)\n",\r
269                 p_child_obj, ib_get_obj_type( p_child_obj ),\r
270                 p_parent_obj, ib_get_obj_type( p_parent_obj ) ) );\r
271 \r
272         /* Insert the object into the parent's object tracking list. */\r
273         p_child_obj->p_ci_ca = p_parent_obj->p_ci_ca;\r
274         cl_spinlock_acquire( &p_parent_obj->lock );\r
275         if( p_parent_obj->state != CL_INITIALIZED )\r
276         {\r
277                 cl_spinlock_release( &p_parent_obj->lock );\r
278                 return IB_INVALID_STATE;\r
279         }\r
280         cl_qlist_insert_tail( &p_parent_obj->obj_list,\r
281                 (cl_list_item_t*)&p_child_obj->pool_item );\r
282         p_child_obj->p_parent_obj = p_parent_obj;\r
283         cl_spinlock_release( &p_parent_obj->lock );\r
284 \r
285         if( p_parent_obj->h_al )\r
286         {\r
287                 if( !p_child_obj->h_al )\r
288                 {\r
289                         p_child_obj->h_al = p_parent_obj->h_al;\r
290 #ifdef CL_KERNEL\r
291                         p_child_obj->hdl = al_hdl_insert_obj( p_child_obj );\r
292                         if( p_child_obj->hdl == AL_INVALID_HANDLE )\r
293                         {\r
294                                 cl_spinlock_acquire( &p_parent_obj->lock );\r
295                                 cl_qlist_remove_item( &p_parent_obj->obj_list,\r
296                                         (cl_list_item_t*)&p_child_obj->pool_item );\r
297                                 p_child_obj->p_parent_obj = NULL;\r
298                                 cl_spinlock_release( &p_parent_obj->lock );\r
299                                 return IB_INSUFFICIENT_MEMORY;\r
300                         }\r
301 #endif\r
302                 }\r
303                 else\r
304                 {\r
305                         CL_ASSERT( p_child_obj->h_al == p_parent_obj->h_al );\r
306                 }\r
307         }\r
308 \r
309         /* Reference the parent. */\r
310         ref_al_obj( p_parent_obj );\r
311         AL_EXIT( AL_DBG_AL_OBJ );\r
312         return IB_SUCCESS;\r
313 }\r
314 \r
315 \r
316 \r
317 /*\r
318  * Called to release a child object from its parent.\r
319  */\r
320 void\r
321 detach_al_obj(\r
322         IN                              al_obj_t * const                        p_obj )\r
323 {\r
324         al_obj_t                                *p_parent_obj;\r
325 \r
326         CL_ENTER( AL_DBG_AL_OBJ, g_al_dbg_lvl );\r
327         \r
328         p_parent_obj = p_obj->p_parent_obj;\r
329         CL_ASSERT( p_obj );\r
330         CL_ASSERT( p_obj->state == CL_INITIALIZED ||\r
331                 p_obj->state == CL_DESTROYING );\r
332         CL_ASSERT( p_parent_obj );\r
333         CL_ASSERT( p_parent_obj->state == CL_INITIALIZED ||\r
334                 p_parent_obj->state == CL_DESTROYING );\r
335 \r
336         CL_TRACE( AL_DBG_AL_OBJ, g_al_dbg_lvl,\r
337                 ("%p(%s) from %p(%s)\n",\r
338                 p_obj, ib_get_obj_type( p_obj ),\r
339                 p_parent_obj, ib_get_obj_type( p_parent_obj ) ) );\r
340 \r
341         /* Remove the object from the parent's list. */\r
342         cl_spinlock_acquire( &p_parent_obj->lock );\r
343         cl_qlist_remove_item( &p_parent_obj->obj_list,\r
344                 (cl_list_item_t*)&p_obj->pool_item );\r
345         cl_spinlock_release( &p_parent_obj->lock );\r
346         CL_EXIT( AL_DBG_AL_OBJ, g_al_dbg_lvl );\r
347 }\r
348 \r
349 \r
350 \r
351 /*\r
352  * Increment a reference count on an object.  This object should not be\r
353  * an object's parent.\r
354  */\r
355 int32_t\r
356 ref_al_obj(\r
357         IN                              al_obj_t * const                        p_obj )\r
358 {\r
359         uint32_t        ref_cnt;\r
360 \r
361         CL_ENTER( AL_DBG_AL_OBJ, g_al_dbg_lvl );\r
362         CL_TRACE( AL_DBG_AL_OBJ, g_al_dbg_lvl,\r
363                 ("%p(%s)\n", p_obj, ib_get_obj_type( p_obj ) ) );\r
364         ref_cnt = cl_atomic_inc( &p_obj->ref_cnt );\r
365         CL_ASSERT( ref_cnt != 1 || p_obj->type == AL_OBJ_TYPE_H_CQ );\r
366 \r
367         CL_EXIT( AL_DBG_AL_OBJ, g_al_dbg_lvl );\r
368         return ref_cnt;\r
369 }\r
370 \r
371 \r
372 \r
373 /*\r
374  * Decrement the reference count on an AL object.  Destroy the object if\r
375  * it is no longer referenced.  This object should not be an object's parent.\r
376  */\r
377 int32_t\r
378 deref_al_obj(\r
379         IN                              al_obj_t * const                        p_obj )\r
380 {\r
381         int32_t                 ref_cnt;\r
382 \r
383         CL_ENTER( AL_DBG_AL_OBJ, g_al_dbg_lvl );\r
384 \r
385         CL_ASSERT( p_obj );\r
386         CL_ASSERT( p_obj->state == CL_INITIALIZED ||\r
387                 p_obj->state == CL_DESTROYING );\r
388         CL_ASSERT( p_obj->ref_cnt );\r
389 \r
390         CL_TRACE( AL_DBG_AL_OBJ, g_al_dbg_lvl,\r
391                 ("%p(%s)\n", p_obj, ib_get_obj_type( p_obj ) ) );\r
392 \r
393         ref_cnt = cl_atomic_dec( &p_obj->ref_cnt );\r
394 \r
395         /* If the reference count went to 0, the object should be destroyed. */\r
396         if( ref_cnt == 0 )\r
397         {\r
398                 if( p_obj->pfn_destroy == async_destroy_obj &&\r
399                         p_obj->user_destroy_cb != ib_sync_destroy )\r
400                 {\r
401                         /* Queue the object for asynchronous destruction. */\r
402 #if AL_OBJ_PRIVATE_ASYNC_PROC\r
403                         cl_async_proc_queue( gp_async_obj_mgr, &p_obj->async_item );\r
404 #else\r
405                         cl_async_proc_queue( gp_async_proc_mgr, &p_obj->async_item );\r
406 #endif\r
407                 }\r
408                 else\r
409                 {\r
410                         /* Signal an event for synchronous destruction. */\r
411                         cl_event_signal( &p_obj->event );\r
412                 }\r
413         }\r
414 \r
415         CL_EXIT( AL_DBG_AL_OBJ, g_al_dbg_lvl );\r
416         return ref_cnt;\r
417 }\r
418 \r
419 \r
420 \r
421 /*\r
422  * Called to cleanup all resources allocated by an object.\r
423  */\r
424 void\r
425 destroy_al_obj(\r
426         IN                              al_obj_t * const                        p_obj )\r
427 {\r
428         CL_ENTER( AL_DBG_AL_OBJ, g_al_dbg_lvl );\r
429 \r
430         CL_ASSERT( p_obj );\r
431         CL_ASSERT( p_obj->state == CL_DESTROYING ||\r
432                 p_obj->state == CL_UNINITIALIZED );\r
433         CL_ASSERT( cl_is_qlist_empty( &p_obj->obj_list ) );\r
434 \r
435         /* Remove the object from the global tracking list. */\r
436         if( p_obj != &gp_al_mgr->obj )\r
437         {\r
438                 cl_spinlock_acquire( &gp_al_mgr->lock );\r
439                 cl_qlist_remove_item( &gp_al_mgr->al_obj_list, &p_obj->list_item );\r
440                 cl_spinlock_release( &gp_al_mgr->lock );\r
441                 deref_al_obj( &gp_al_mgr->obj );\r
442         }\r
443 \r
444         cl_event_destroy( &p_obj->event );\r
445         cl_spinlock_destroy( &p_obj->lock );\r
446         p_obj->state = CL_DESTROYED;\r
447 \r
448         CL_EXIT( AL_DBG_AL_OBJ, g_al_dbg_lvl );\r
449 }\r
450 \r
451 \r
452 \r
453 void\r
454 async_destroy_obj(\r
455         IN                              struct _al_obj                          *p_obj,\r
456         IN              const   ib_pfn_destroy_cb_t                     pfn_destroy_cb )\r
457 {\r
458         CL_ENTER( AL_DBG_AL_OBJ, g_al_dbg_lvl );\r
459 \r
460         if( pfn_destroy_cb == ib_sync_destroy )\r
461                 sync_destroy_obj( p_obj, pfn_destroy_cb );\r
462         else if( destroy_obj( p_obj, pfn_destroy_cb ) )\r
463                 deref_al_obj( p_obj );  /* Only destroy the object once. */\r
464 \r
465         CL_EXIT( AL_DBG_AL_OBJ, g_al_dbg_lvl );\r
466 }\r
467 \r
468 \r
469 \r
470 void\r
471 sync_destroy_obj(\r
472         IN                              struct _al_obj                          *p_obj,\r
473         IN              const   ib_pfn_destroy_cb_t                     pfn_destroy_cb )\r
474 {\r
475         cl_status_t             cl_status;\r
476 \r
477         CL_ENTER( AL_DBG_AL_OBJ, g_al_dbg_lvl );\r
478 \r
479         if( !destroy_obj( p_obj, pfn_destroy_cb ) )\r
480         {\r
481                 /* Object is already being destroyed... */\r
482                 AL_EXIT( AL_DBG_AL_OBJ );\r
483                 return;\r
484         }\r
485 \r
486         if( deref_al_obj( p_obj ) )\r
487         {\r
488         #ifdef _DEBUG_\r
489                 uint32_t                wait_us;\r
490                 /*\r
491                  * Wait for all other references to go away.  We wait as long as the\r
492                  * longest child will take, plus an additional amount based on the\r
493                  * number of descendants.\r
494                  */\r
495                 wait_us = (p_obj->timeout_ms * 1000) +\r
496                         (AL_TIMEOUT_PER_DESC_US * p_obj->desc_cnt);\r
497                 wait_us = MIN( wait_us, AL_MAX_TIMEOUT_US );\r
498                 do\r
499                 {\r
500                         cl_status = cl_event_wait_on(\r
501                                 &p_obj->event, wait_us, AL_WAIT_ALERTABLE );\r
502                 } while( cl_status == CL_NOT_DONE );\r
503 \r
504                 if( cl_status != CL_SUCCESS )\r
505                 {\r
506                         CL_TRACE( AL_DBG_ERROR, g_al_dbg_lvl,\r
507                                 ("Error waiting for references to be released - delaying.\n") );\r
508                         print_al_obj( p_obj );\r
509                         /*\r
510                          * Wait some more to handle really long timeouts by referencing\r
511                          * objects that are not descendants.\r
512                          */\r
513                         do\r
514                         {\r
515                                 cl_status = cl_event_wait_on(\r
516                                         &p_obj->event, AL_MAX_TIMEOUT_US, AL_WAIT_ALERTABLE );\r
517                         } while( cl_status == CL_NOT_DONE );\r
518                 }\r
519         #else\r
520                 do\r
521                 {\r
522                         cl_status = cl_event_wait_on(\r
523                                 &p_obj->event, EVENT_NO_TIMEOUT, AL_WAIT_ALERTABLE );\r
524                 } while( cl_status == CL_NOT_DONE );\r
525         #endif\r
526                 CL_ASSERT( cl_status == CL_SUCCESS );\r
527                 if( cl_status != CL_SUCCESS )\r
528                 {\r
529                         CL_TRACE( AL_DBG_ERROR, g_al_dbg_lvl,\r
530                                 ("Forcing object destruction.\n") );\r
531                         print_al_obj( p_obj );\r
532                         //print_tail_al_objs();\r
533                         print_al_objs( p_obj->h_al );\r
534                         p_obj->ref_cnt = 0;\r
535                 }\r
536         }\r
537         async_destroy_cb( &p_obj->async_item );\r
538 \r
539         CL_EXIT( AL_DBG_AL_OBJ, g_al_dbg_lvl );\r
540 }\r
541 \r
542 \r
543 \r
544 boolean_t\r
545 destroy_obj(\r
546         IN                              struct _al_obj                          *p_obj,\r
547         IN              const   ib_pfn_destroy_cb_t                     pfn_destroy_cb )\r
548 {\r
549         cl_list_item_t                  *p_list_item;\r
550         al_obj_t                                *p_child_obj;\r
551 \r
552         CL_ENTER( AL_DBG_AL_OBJ, g_al_dbg_lvl );\r
553 \r
554         CL_ASSERT( p_obj );\r
555         CL_ASSERT( p_obj->state == CL_INITIALIZED ||\r
556                 p_obj->state == CL_DESTROYING );\r
557 \r
558         CL_TRACE( AL_DBG_AL_OBJ, g_al_dbg_lvl,\r
559                 ("%p(%s)\n", p_obj, ib_get_obj_type( p_obj ) ) );\r
560 \r
561         /*\r
562          * Lock to synchronize with asynchronous event processing.\r
563          * See ci_ca_async_event_cb for more information.\r
564          */\r
565         cl_spinlock_acquire( &p_obj->lock );\r
566         if( p_obj->state == CL_DESTROYING )\r
567         {\r
568                 cl_spinlock_release( &p_obj->lock );\r
569                 deref_al_obj( p_obj );\r
570                 AL_EXIT( AL_DBG_AL_OBJ );\r
571                 return FALSE;\r
572         }\r
573         p_obj->state = CL_DESTROYING;\r
574         cl_spinlock_release( &p_obj->lock );\r
575         deref_al_obj( p_obj );\r
576 \r
577         /* Notify the object that it is being destroyed. */\r
578         if( p_obj->pfn_destroying )\r
579                 p_obj->pfn_destroying( p_obj );\r
580 \r
581 #ifdef CL_KERNEL\r
582         /* Release this object's handle. */\r
583         if( p_obj->hdl != AL_INVALID_HANDLE )\r
584         {\r
585                 CL_ASSERT( p_obj->h_al );\r
586                 al_hdl_free_obj( p_obj );\r
587         }\r
588 #endif\r
589 \r
590         if( p_obj->p_parent_obj )\r
591                 detach_al_obj( p_obj );\r
592 \r
593         /*      Destroy all child resources.  No need to lock during destruction. */\r
594         CL_TRACE( AL_DBG_AL_OBJ, g_al_dbg_lvl, ("destroying children\n") );\r
595         p_list_item = cl_qlist_tail( &p_obj->obj_list );\r
596         while( p_list_item != cl_qlist_end( &p_obj->obj_list ) )\r
597         {\r
598                 p_child_obj = PARENT_STRUCT( p_list_item, al_obj_t, pool_item );\r
599                 CL_ASSERT( p_child_obj->pfn_destroy );\r
600                 CL_TRACE( AL_DBG_AL_OBJ, g_al_dbg_lvl,\r
601                         ("bye bye: %p(%s)\n", p_child_obj,\r
602                         ib_get_obj_type( p_child_obj ) ) );\r
603                 ref_al_obj( p_child_obj );\r
604                 p_child_obj->pfn_destroy( p_child_obj, NULL );\r
605 \r
606                 p_list_item = cl_qlist_tail( &p_obj->obj_list );\r
607         }\r
608 \r
609         /*\r
610          * Update our parent's timeout value.  Ours could have been increased\r
611          * when destroying one of our children's.\r
612          */\r
613         if( p_obj->p_parent_obj )\r
614         {\r
615                 set_al_obj_timeout( p_obj->p_parent_obj, p_obj->timeout_ms );\r
616                 inc_al_obj_desc( p_obj->p_parent_obj, p_obj->desc_cnt + 1 );\r
617         }\r
618 \r
619         p_obj->user_destroy_cb = pfn_destroy_cb;\r
620         CL_EXIT( AL_DBG_AL_OBJ, g_al_dbg_lvl );\r
621         return TRUE;\r
622 }\r
623 \r
624 \r
625 \r
626 void\r
627 async_destroy_cb(\r
628         IN                              cl_async_proc_item_t            *p_item )\r
629 {\r
630         al_obj_t                                *p_obj;\r
631         al_obj_t                                *p_parent_obj = NULL;\r
632 \r
633         CL_ENTER( AL_DBG_AL_OBJ, g_al_dbg_lvl );\r
634 \r
635         CL_ASSERT( p_item );\r
636         p_obj = PARENT_STRUCT( p_item, al_obj_t, async_item );\r
637         CL_ASSERT( p_obj );\r
638         CL_ASSERT( p_obj->state == CL_DESTROYING );\r
639         CL_ASSERT( !p_obj->ref_cnt );\r
640 \r
641         CL_TRACE( AL_DBG_AL_OBJ, g_al_dbg_lvl,\r
642                 ("%p\n", p_obj ) );\r
643 \r
644         /* Cleanup any hardware related resources. */\r
645         if( p_obj->pfn_cleanup )\r
646         {\r
647                 CL_TRACE( AL_DBG_AL_OBJ, g_al_dbg_lvl, ("cleaning up\n" ) );\r
648                 p_obj->pfn_cleanup( p_obj );\r
649         }\r
650 \r
651         /* We can now safely dereference the parent. */\r
652         if( p_obj->p_parent_obj )\r
653         {\r
654                 p_parent_obj = p_obj->p_parent_obj;\r
655                 p_obj->p_parent_obj = NULL;\r
656         }\r
657 \r
658         /* Notify the user that we're done. */\r
659         if( p_obj->user_destroy_cb )\r
660         {\r
661                 CL_TRACE( AL_DBG_AL_OBJ, g_al_dbg_lvl, ("notifying user\n" ) );\r
662                 p_obj->user_destroy_cb( (void*)p_obj->context );\r
663         }\r
664 \r
665         /* Free the resources associated with the object. */\r
666         CL_TRACE( AL_DBG_AL_OBJ, g_al_dbg_lvl, ("freeing object\n" ) );\r
667         p_obj->pfn_free( p_obj );\r
668 \r
669         /* Dereference the parent after freeing the child. */\r
670         if( p_parent_obj )\r
671                 deref_al_obj( p_parent_obj );\r
672         CL_EXIT( AL_DBG_AL_OBJ, g_al_dbg_lvl );\r
673 }\r