2 * Copyright (c) 2005 SilverStorm Technologies. All rights reserved.
\r
3 * Copyright (c) 1996-2003 Intel Corporation. All rights reserved.
\r
5 * This software is available to you under the OpenIB.org BSD license
\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
12 * - Redistributions of source code must retain the above
\r
13 * copyright notice, this list of conditions and the following
\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
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
34 #include "al_ci_ca.h"
\r
35 #include "al_common.h"
\r
36 #include "al_debug.h"
\r
38 #if defined(EVENT_TRACING)
\r
42 #include "al_common.tmh"
\r
46 #include <complib/cl_math.h>
\r
47 #include "ib_common.h"
\r
51 #if AL_OBJ_PRIVATE_ASYNC_PROC
\r
52 cl_async_proc_t *gp_async_obj_mgr = NULL;
\r
58 IN struct _al_obj *p_obj,
\r
59 IN const ib_pfn_destroy_cb_t pfn_destroy_cb );
\r
64 IN cl_async_proc_item_t *p_item );
\r
69 IN struct _al_obj *p_obj,
\r
70 IN const ib_pfn_destroy_cb_t pfn_destroy_cb );
\r
75 IN struct _al_obj *p_obj,
\r
76 IN const ib_pfn_destroy_cb_t pfn_destroy_cb );
\r
80 const char* ib_obj_type_str[] =
\r
82 "AL_OBJ_TYPE_UNKNOWN",
\r
91 "AL_OBJ_TYPE_H_CONN",
\r
92 "AL_OBJ_TYPE_H_LISTEN",
\r
93 "AL_OBJ_TYPE_H_IOC",
\r
94 "AL_OBJ_TYPE_H_SVC_ENTRY",
\r
95 "AL_OBJ_TYPE_H_PNP",
\r
96 "AL_OBJ_TYPE_H_SA_REQ",
\r
97 "AL_OBJ_TYPE_H_MCAST",
\r
98 "AL_OBJ_TYPE_H_ATTACH",
\r
99 "AL_OBJ_TYPE_H_MAD",
\r
100 "AL_OBJ_TYPE_H_MAD_POOL",
\r
101 "AL_OBJ_TYPE_H_POOL_KEY",
\r
102 "AL_OBJ_TYPE_H_MAD_SVC",
\r
103 "AL_OBJ_TYPE_CI_CA",
\r
108 "AL_OBJ_TYPE_LOADER",
\r
109 "AL_OBJ_TYPE_MAD_POOL",
\r
110 "AL_OBJ_TYPE_MAD_DISP",
\r
111 "AL_OBJ_TYPE_AL_MGR",
\r
112 "AL_OBJ_TYPE_PNP_MGR",
\r
113 "AL_OBJ_TYPE_IOC_PNP_MGR",
\r
114 "AL_OBJ_TYPE_IOC_PNP_SVC",
\r
115 "AL_OBJ_TYPE_QUERY_SVC",
\r
116 "AL_OBJ_TYPE_MCAST_SVC",
\r
117 "AL_OBJ_TYPE_SA_REQ_SVC",
\r
118 "AL_OBJ_TYPE_RES_MGR",
\r
119 "AL_OBJ_TYPE_H_CA_ATTR",
\r
120 "AL_OBJ_TYPE_H_PNP_EVENT",
\r
121 "AL_OBJ_TYPE_H_SA_REG",
\r
122 "AL_OBJ_TYPE_H_FMR"
\r
127 * Used to force synchronous destruction of AL objects.
\r
133 UNUSED_PARAM( context );
\r
139 IN al_obj_t * const p_obj,
\r
140 IN const al_obj_type_t obj_type )
\r
142 CL_ASSERT( p_obj );
\r
143 cl_memclr( p_obj, sizeof( al_obj_t ) );
\r
145 cl_spinlock_construct( &p_obj->lock );
\r
146 p_obj->state = CL_UNINITIALIZED;
\r
147 p_obj->type = obj_type;
\r
148 p_obj->timeout_ms = AL_DEFAULT_TIMEOUT_MS;
\r
149 p_obj->ref_cnt = 1;
\r
150 cl_event_construct( &p_obj->event );
\r
152 /* Insert the object into the global tracking list. */
\r
153 if( p_obj != &gp_al_mgr->obj )
\r
155 cl_spinlock_acquire( &gp_al_mgr->lock );
\r
156 cl_qlist_insert_tail( &gp_al_mgr->al_obj_list, &p_obj->list_item );
\r
157 cl_spinlock_release( &gp_al_mgr->lock );
\r
158 ref_al_obj( &gp_al_mgr->obj );
\r
166 IN al_obj_t * const p_obj,
\r
167 IN const void* const context,
\r
168 IN boolean_t async_destroy,
\r
169 IN const al_pfn_destroying_t pfn_destroying,
\r
170 IN const al_pfn_cleanup_t pfn_cleanup,
\r
171 IN const al_pfn_free_t pfn_free )
\r
173 cl_status_t cl_status;
\r
175 AL_ENTER( AL_DBG_AL_OBJ );
\r
176 CL_ASSERT( p_obj && pfn_free );
\r
177 CL_ASSERT( p_obj->state == CL_UNINITIALIZED );
\r
178 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_AL_OBJ,
\r
179 ("%016I64x\n", (LONG_PTR)p_obj ) );
\r
181 /* Initialize the object. */
\r
182 p_obj->async_item.pfn_callback = async_destroy_cb;
\r
183 p_obj->pfn_free = pfn_free;
\r
185 p_obj->context = context;
\r
187 if( async_destroy && !(p_obj->type & AL_OBJ_SUBTYPE_UM_EXPORT) )
\r
188 p_obj->pfn_destroy = async_destroy_obj;
\r
190 p_obj->pfn_destroy = sync_destroy_obj;
\r
192 p_obj->pfn_destroying = pfn_destroying;
\r
194 p_obj->pfn_cleanup = pfn_cleanup;
\r
195 p_obj->user_destroy_cb = NULL;
\r
197 cl_qlist_init( &p_obj->obj_list );
\r
198 cl_status = cl_spinlock_init( &p_obj->lock );
\r
199 if( cl_status != CL_SUCCESS )
\r
201 return ib_convert_cl_status( cl_status );
\r
204 cl_status = cl_event_init( &p_obj->event, FALSE );
\r
205 if( cl_status != CL_SUCCESS )
\r
207 return ib_convert_cl_status( cl_status );
\r
210 p_obj->state = CL_INITIALIZED;
\r
213 * Hold an extra reference on the object until creation is complete.
\r
214 * This prevents a client's destruction of the object during asynchronous
\r
215 * event callback processing from deallocating the object before the
\r
216 * creation is complete.
\r
218 ref_al_obj( p_obj );
\r
220 AL_EXIT( AL_DBG_AL_OBJ );
\r
227 IN al_obj_t * const p_obj )
\r
229 CL_ASSERT( p_obj && (p_obj->ref_cnt == 0) );
\r
230 CL_ASSERT( p_obj->state == CL_DESTROYING );
\r
232 p_obj->ref_cnt = 1;
\r
233 p_obj->desc_cnt = 0;
\r
234 p_obj->state = CL_INITIALIZED;
\r
235 p_obj->h_al = NULL;
\r
236 p_obj->hdl = AL_INVALID_HANDLE;
\r
242 set_al_obj_timeout(
\r
243 IN al_obj_t * const p_obj,
\r
244 IN const uint32_t timeout_ms )
\r
246 CL_ASSERT( p_obj );
\r
248 /* Only increase timeout values. */
\r
249 p_obj->timeout_ms = MAX( p_obj->timeout_ms, timeout_ms );
\r
256 IN al_obj_t * const p_obj,
\r
257 IN const uint32_t desc_cnt )
\r
259 CL_ASSERT( p_obj );
\r
261 /* Increment the number of descendants. */
\r
262 p_obj->desc_cnt += desc_cnt;
\r
269 IN al_obj_t * const p_parent_obj,
\r
270 IN al_obj_t * const p_child_obj )
\r
272 AL_ENTER( AL_DBG_AL_OBJ );
\r
274 CL_ASSERT( p_child_obj->state == CL_INITIALIZED );
\r
276 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_AL_OBJ,
\r
277 ("%016I64x(%s) to %016I64x(%s)\n",
\r
278 (LONG_PTR)p_child_obj, ib_get_obj_type( p_child_obj ),
\r
279 (LONG_PTR)p_parent_obj, ib_get_obj_type( p_parent_obj ) ) );
\r
281 /* Insert the object into the parent's object tracking list. */
\r
282 p_child_obj->p_ci_ca = p_parent_obj->p_ci_ca;
\r
283 cl_spinlock_acquire( &p_parent_obj->lock );
\r
284 if( p_parent_obj->state != CL_INITIALIZED )
\r
286 cl_spinlock_release( &p_parent_obj->lock );
\r
287 return IB_INVALID_STATE;
\r
289 cl_qlist_insert_tail( &p_parent_obj->obj_list,
\r
290 (cl_list_item_t*)&p_child_obj->pool_item );
\r
291 p_child_obj->p_parent_obj = p_parent_obj;
\r
292 cl_spinlock_release( &p_parent_obj->lock );
\r
294 if( p_parent_obj->h_al )
\r
296 if( !p_child_obj->h_al )
\r
298 p_child_obj->h_al = p_parent_obj->h_al;
\r
300 p_child_obj->hdl = al_hdl_insert_obj( p_child_obj );
\r
301 if( p_child_obj->hdl == AL_INVALID_HANDLE )
\r
303 cl_spinlock_acquire( &p_parent_obj->lock );
\r
304 cl_qlist_remove_item( &p_parent_obj->obj_list,
\r
305 (cl_list_item_t*)&p_child_obj->pool_item );
\r
306 p_child_obj->p_parent_obj = NULL;
\r
307 cl_spinlock_release( &p_parent_obj->lock );
\r
308 return IB_INSUFFICIENT_MEMORY;
\r
314 CL_ASSERT( p_child_obj->h_al == p_parent_obj->h_al );
\r
318 /* Reference the parent. */
\r
319 ref_al_obj( p_parent_obj );
\r
320 AL_EXIT( AL_DBG_AL_OBJ );
\r
327 * Called to release a child object from its parent.
\r
331 IN al_obj_t * const p_obj )
\r
333 al_obj_t *p_parent_obj;
\r
335 AL_ENTER( AL_DBG_AL_OBJ );
\r
337 p_parent_obj = p_obj->p_parent_obj;
\r
338 CL_ASSERT( p_obj );
\r
339 CL_ASSERT( p_obj->state == CL_INITIALIZED ||
\r
340 p_obj->state == CL_DESTROYING );
\r
341 CL_ASSERT( p_parent_obj );
\r
342 CL_ASSERT( p_parent_obj->state == CL_INITIALIZED ||
\r
343 p_parent_obj->state == CL_DESTROYING );
\r
345 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_AL_OBJ,
\r
346 ("%016I64x(%s) from %016I64x(%s)\n",
\r
347 (LONG_PTR)p_obj, ib_get_obj_type( p_obj ),
\r
348 (LONG_PTR)p_parent_obj, ib_get_obj_type( p_parent_obj ) ) );
\r
350 /* Remove the object from the parent's list. */
\r
351 cl_spinlock_acquire( &p_parent_obj->lock );
\r
352 cl_qlist_remove_item( &p_parent_obj->obj_list,
\r
353 (cl_list_item_t*)&p_obj->pool_item );
\r
354 cl_spinlock_release( &p_parent_obj->lock );
\r
355 AL_EXIT( AL_DBG_AL_OBJ );
\r
361 * Increment a reference count on an object. This object should not be
\r
362 * an object's parent.
\r
366 IN al_obj_t * const p_obj )
\r
370 AL_ENTER( AL_DBG_AL_OBJ );
\r
371 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_AL_OBJ,
\r
372 ("%016I64x(%s)\n", (LONG_PTR)p_obj, ib_get_obj_type( p_obj ) ) );
\r
373 ref_cnt = cl_atomic_inc( &p_obj->ref_cnt );
\r
374 CL_ASSERT( ref_cnt != 1 || p_obj->type == AL_OBJ_TYPE_H_CQ );
\r
376 AL_EXIT( AL_DBG_AL_OBJ );
\r
383 * Decrement the reference count on an AL object. Destroy the object if
\r
384 * it is no longer referenced. This object should not be an object's parent.
\r
388 IN al_obj_t * const p_obj )
\r
392 AL_ENTER( AL_DBG_AL_OBJ );
\r
394 CL_ASSERT( p_obj );
\r
395 CL_ASSERT( p_obj->state == CL_INITIALIZED ||
\r
396 p_obj->state == CL_DESTROYING );
\r
397 CL_ASSERT( p_obj->ref_cnt );
\r
399 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_AL_OBJ,
\r
400 ("%016I64x(%s)\n", (LONG_PTR)p_obj, ib_get_obj_type( p_obj ) ) );
\r
402 ref_cnt = cl_atomic_dec( &p_obj->ref_cnt );
\r
404 /* If the reference count went to 0, the object should be destroyed. */
\r
407 if( p_obj->pfn_destroy == async_destroy_obj &&
\r
408 p_obj->user_destroy_cb != ib_sync_destroy )
\r
410 /* Queue the object for asynchronous destruction. */
\r
411 #if AL_OBJ_PRIVATE_ASYNC_PROC
\r
412 cl_async_proc_queue( gp_async_obj_mgr, &p_obj->async_item );
\r
414 cl_async_proc_queue( gp_async_proc_mgr, &p_obj->async_item );
\r
419 /* Signal an event for synchronous destruction. */
\r
420 cl_event_signal( &p_obj->event );
\r
424 AL_EXIT( AL_DBG_AL_OBJ );
\r
431 * Called to cleanup all resources allocated by an object.
\r
435 IN al_obj_t * const p_obj )
\r
437 AL_ENTER( AL_DBG_AL_OBJ );
\r
439 CL_ASSERT( p_obj );
\r
440 CL_ASSERT( p_obj->state == CL_DESTROYING ||
\r
441 p_obj->state == CL_UNINITIALIZED );
\r
442 CL_ASSERT( cl_is_qlist_empty( &p_obj->obj_list ) );
\r
444 /* Remove the object from the global tracking list. */
\r
445 if( p_obj != &gp_al_mgr->obj )
\r
447 cl_spinlock_acquire( &gp_al_mgr->lock );
\r
448 cl_qlist_remove_item( &gp_al_mgr->al_obj_list, &p_obj->list_item );
\r
449 cl_spinlock_release( &gp_al_mgr->lock );
\r
450 deref_al_obj( &gp_al_mgr->obj );
\r
453 cl_event_destroy( &p_obj->event );
\r
454 cl_spinlock_destroy( &p_obj->lock );
\r
455 p_obj->state = CL_DESTROYED;
\r
457 AL_EXIT( AL_DBG_AL_OBJ );
\r
464 IN struct _al_obj *p_obj,
\r
465 IN const ib_pfn_destroy_cb_t pfn_destroy_cb )
\r
467 AL_ENTER( AL_DBG_AL_OBJ );
\r
469 if( pfn_destroy_cb == ib_sync_destroy )
\r
470 sync_destroy_obj( p_obj, pfn_destroy_cb );
\r
471 else if( destroy_obj( p_obj, pfn_destroy_cb ) )
\r
472 deref_al_obj( p_obj ); /* Only destroy the object once. */
\r
474 AL_EXIT( AL_DBG_AL_OBJ );
\r
481 IN struct _al_obj *p_obj,
\r
482 IN const ib_pfn_destroy_cb_t pfn_destroy_cb )
\r
484 cl_status_t cl_status;
\r
486 AL_ENTER( AL_DBG_AL_OBJ );
\r
488 if( !destroy_obj( p_obj, pfn_destroy_cb ) )
\r
490 /* Object is already being destroyed... */
\r
491 AL_EXIT( AL_DBG_AL_OBJ );
\r
495 if( deref_al_obj( p_obj ) )
\r
500 * Wait for all other references to go away. We wait as long as the
\r
501 * longest child will take, plus an additional amount based on the
\r
502 * number of descendants.
\r
504 wait_us = (p_obj->timeout_ms * 1000) +
\r
505 (AL_TIMEOUT_PER_DESC_US * p_obj->desc_cnt);
\r
506 wait_us = MIN( wait_us, AL_MAX_TIMEOUT_US );
\r
509 cl_status = cl_event_wait_on(
\r
510 &p_obj->event, wait_us, AL_WAIT_ALERTABLE );
\r
511 } while( cl_status == CL_NOT_DONE );
\r
513 if( cl_status != CL_SUCCESS )
\r
515 AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,
\r
516 ("Error waiting for references to be released - delaying.\n") );
\r
517 print_al_obj( p_obj );
\r
519 * Wait some more to handle really long timeouts by referencing
\r
520 * objects that are not descendants.
\r
524 cl_status = cl_event_wait_on(
\r
525 &p_obj->event, AL_MAX_TIMEOUT_US, AL_WAIT_ALERTABLE );
\r
526 } while( cl_status == CL_NOT_DONE );
\r
531 cl_status = cl_event_wait_on(
\r
532 &p_obj->event, EVENT_NO_TIMEOUT, AL_WAIT_ALERTABLE );
\r
533 } while( cl_status == CL_NOT_DONE );
\r
535 CL_ASSERT( cl_status == CL_SUCCESS );
\r
536 if( cl_status != CL_SUCCESS )
\r
538 AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,
\r
539 ("Forcing object destruction.\n") );
\r
540 print_al_obj( p_obj );
\r
541 //print_tail_al_objs();
\r
542 print_al_objs( p_obj->h_al );
\r
543 p_obj->ref_cnt = 0;
\r
546 async_destroy_cb( &p_obj->async_item );
\r
548 AL_EXIT( AL_DBG_AL_OBJ );
\r
555 IN struct _al_obj *p_obj,
\r
556 IN const ib_pfn_destroy_cb_t pfn_destroy_cb )
\r
558 cl_list_item_t *p_list_item;
\r
559 al_obj_t *p_child_obj;
\r
561 AL_ENTER( AL_DBG_AL_OBJ );
\r
563 CL_ASSERT( p_obj );
\r
564 CL_ASSERT( p_obj->state == CL_INITIALIZED ||
\r
565 p_obj->state == CL_DESTROYING );
\r
567 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_AL_OBJ,
\r
568 ("%016I64x(%s)\n", (LONG_PTR)p_obj, ib_get_obj_type( p_obj ) ) );
\r
571 * Lock to synchronize with asynchronous event processing.
\r
572 * See ci_ca_async_event_cb for more information.
\r
574 cl_spinlock_acquire( &p_obj->lock );
\r
575 if( p_obj->state == CL_DESTROYING )
\r
577 cl_spinlock_release( &p_obj->lock );
\r
578 deref_al_obj( p_obj );
\r
579 AL_EXIT( AL_DBG_AL_OBJ );
\r
582 p_obj->state = CL_DESTROYING;
\r
583 cl_spinlock_release( &p_obj->lock );
\r
584 deref_al_obj( p_obj );
\r
586 /* Notify the object that it is being destroyed. */
\r
587 if( p_obj->pfn_destroying )
\r
588 p_obj->pfn_destroying( p_obj );
\r
591 /* Release this object's handle. */
\r
592 if( p_obj->hdl != AL_INVALID_HANDLE )
\r
594 CL_ASSERT( p_obj->h_al );
\r
595 al_hdl_free_obj( p_obj );
\r
599 if( p_obj->p_parent_obj )
\r
600 detach_al_obj( p_obj );
\r
602 /* Destroy all child resources. No need to lock during destruction. */
\r
603 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_AL_OBJ, ("destroying children\n") );
\r
604 p_list_item = cl_qlist_tail( &p_obj->obj_list );
\r
605 while( p_list_item != cl_qlist_end( &p_obj->obj_list ) )
\r
607 p_child_obj = PARENT_STRUCT( p_list_item, al_obj_t, pool_item );
\r
608 CL_ASSERT( p_child_obj->pfn_destroy );
\r
609 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_AL_OBJ,
\r
610 ("bye bye: %016I64x(%s)\n", (LONG_PTR)p_child_obj,
\r
611 ib_get_obj_type( p_child_obj ) ) );
\r
612 ref_al_obj( p_child_obj );
\r
613 p_child_obj->pfn_destroy( p_child_obj, NULL );
\r
615 p_list_item = cl_qlist_tail( &p_obj->obj_list );
\r
619 * Update our parent's timeout value. Ours could have been increased
\r
620 * when destroying one of our children's.
\r
622 if( p_obj->p_parent_obj )
\r
624 set_al_obj_timeout( p_obj->p_parent_obj, p_obj->timeout_ms );
\r
625 inc_al_obj_desc( p_obj->p_parent_obj, p_obj->desc_cnt + 1 );
\r
628 p_obj->user_destroy_cb = pfn_destroy_cb;
\r
629 AL_EXIT( AL_DBG_AL_OBJ );
\r
637 IN cl_async_proc_item_t *p_item )
\r
640 al_obj_t *p_parent_obj = NULL;
\r
642 AL_ENTER( AL_DBG_AL_OBJ );
\r
644 CL_ASSERT( p_item );
\r
645 p_obj = PARENT_STRUCT( p_item, al_obj_t, async_item );
\r
646 CL_ASSERT( p_obj );
\r
647 CL_ASSERT( p_obj->state == CL_DESTROYING );
\r
648 CL_ASSERT( !p_obj->ref_cnt );
\r
650 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_AL_OBJ,
\r
651 ("%016I64x\n", (LONG_PTR)p_obj ) );
\r
653 /* Cleanup any hardware related resources. */
\r
654 if( p_obj->pfn_cleanup )
\r
656 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_AL_OBJ, ("cleaning up\n" ) );
\r
657 p_obj->pfn_cleanup( p_obj );
\r
660 /* We can now safely dereference the parent. */
\r
661 if( p_obj->p_parent_obj )
\r
663 p_parent_obj = p_obj->p_parent_obj;
\r
664 p_obj->p_parent_obj = NULL;
\r
667 /* Notify the user that we're done. */
\r
668 if( p_obj->user_destroy_cb )
\r
670 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_AL_OBJ, ("notifying user\n" ) );
\r
671 p_obj->user_destroy_cb( (void*)p_obj->context );
\r
674 /* Free the resources associated with the object. */
\r
675 AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_AL_OBJ, ("freeing object\n" ) );
\r
676 p_obj->pfn_free( p_obj );
\r
678 /* Dereference the parent after freeing the child. */
\r
680 deref_al_obj( p_parent_obj );
\r
681 AL_EXIT( AL_DBG_AL_OBJ );
\r