[IBAL] Fix locking around MAD tracking list.
[mirror/winof/.git] / core / al / kernel / al_dev.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 <iba/ib_al.h>\r
34 #include <complib/cl_qmap.h>\r
35 #include <complib/cl_memory.h>\r
36 #include <complib/cl_qpool.h>\r
37 #include <complib/cl_passivelock.h>\r
38 #include <complib/cl_vector.h>\r
39 #include <complib/cl_spinlock.h>\r
40 \r
41 #include "al.h"\r
42 #include "al_ca.h"\r
43 #include "al_common.h"\r
44 #include "al_cq.h"\r
45 #include "al_debug.h"\r
46 #if defined(EVENT_TRACING)\r
47 #ifdef offsetof\r
48 #undef offsetof\r
49 #endif\r
50 #include "al_dev.tmh"\r
51 #endif\r
52 #include "al_dev.h"\r
53 #include "al_qp.h"\r
54 #include "al_mgr.h"\r
55 #include "al_proxy.h"\r
56 \r
57 \r
58 \r
59 static cl_status_t\r
60 __proxy_reg_pnp(\r
61         IN              al_dev_open_context_t                   *p_context );\r
62 \r
63 static void\r
64 __proxy_cancel_cblists(\r
65         IN              al_dev_open_context_t                   *p_context );\r
66 \r
67 \r
68 \r
69 \r
70 static void\r
71 __construct_open_context(\r
72         IN              al_dev_open_context_t                   *p_context )\r
73 {\r
74         cl_event_construct( &p_context->close_event );\r
75 \r
76         cl_qpool_construct( &p_context->cb_pool );\r
77         cl_spinlock_construct( &p_context->cb_pool_lock );\r
78 \r
79         cl_qlist_init( &p_context->cm_cb_list );\r
80         cl_qlist_init( &p_context->comp_cb_list );\r
81         cl_qlist_init( &p_context->misc_cb_list );\r
82         cl_spinlock_construct( &p_context->cb_lock );\r
83         cl_mutex_construct( &p_context->pnp_mutex );\r
84 }\r
85 \r
86 \r
87 \r
88 /*\r
89  * Initialize all objects used by the per client open context.\r
90  */\r
91 static cl_status_t\r
92 __init_open_context(\r
93         IN              al_dev_open_context_t                   *p_context )\r
94 {\r
95         cl_status_t             cl_status;\r
96 \r
97         cl_status = cl_event_init( &p_context->close_event, FALSE );\r
98         if( cl_status != CL_SUCCESS )\r
99                 return cl_status;\r
100 \r
101         /* Allocate pool for storing callback info or requests. */\r
102         cl_status = cl_qpool_init( &p_context->cb_pool,\r
103                 AL_CB_POOL_START_SIZE, 0, AL_CB_POOL_GROW_SIZE,\r
104                 sizeof(al_proxy_cb_info_t), NULL, NULL, NULL );\r
105         if( cl_status != CL_SUCCESS )\r
106                 return cl_status;\r
107 \r
108         cl_status = cl_spinlock_init( &p_context->cb_pool_lock );\r
109         if( cl_status != CL_SUCCESS )\r
110                 return cl_status;\r
111 \r
112         cl_status = cl_spinlock_init( &p_context->cb_lock );\r
113         if( cl_status != CL_SUCCESS )\r
114                 return cl_status;\r
115 \r
116         cl_status = cl_mutex_init( &p_context->pnp_mutex );\r
117         if( cl_status != CL_SUCCESS )\r
118                 return cl_status;\r
119 \r
120         return CL_SUCCESS;\r
121 }\r
122 \r
123 \r
124 \r
125 static void\r
126 __destroy_open_context(\r
127         IN              al_dev_open_context_t                   *p_context )\r
128 {\r
129         cl_event_destroy( &p_context->close_event );\r
130 \r
131         cl_qpool_destroy( &p_context->cb_pool );\r
132         cl_spinlock_destroy( &p_context->cb_pool_lock );\r
133         cl_spinlock_destroy( &p_context->cb_lock );\r
134         cl_mutex_destroy( &p_context->pnp_mutex );\r
135 }\r
136 \r
137 \r
138 \r
139 cl_status_t\r
140 al_dev_open(\r
141         IN                              cl_ioctl_handle_t                       h_ioctl )\r
142 {\r
143         al_dev_open_context_t   *p_context;\r
144         ib_api_status_t                 status;\r
145         cl_status_t                             cl_status;\r
146         IO_STACK_LOCATION               *p_io_stack;\r
147         ULONG                                   *p_ver;\r
148 \r
149         AL_ENTER( AL_DBG_DEV );\r
150 \r
151         p_io_stack = IoGetCurrentIrpStackLocation( h_ioctl );\r
152 \r
153         p_ver = cl_ioctl_in_buf( h_ioctl );\r
154 \r
155         if( p_io_stack->FileObject->FsContext ||\r
156                 cl_ioctl_in_size( h_ioctl ) != sizeof(ULONG) ||\r
157                 !p_ver ||\r
158                 cl_ioctl_out_size( h_ioctl ) )\r
159         {\r
160                 AL_PRINT_EXIT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
161                         ("context already exists or bad parameters.\n") );\r
162                 return CL_INVALID_PARAMETER;\r
163         }\r
164 \r
165         if( *p_ver != AL_IOCTL_VERSION )\r
166         {\r
167                 AL_PRINT_EXIT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
168                         ("Unsupported client version: %d\n", *p_ver) );\r
169                 return CL_INVALID_PARAMETER;\r
170         }\r
171 \r
172         /* Allocate the client's context structure. */\r
173         p_context = (al_dev_open_context_t*)\r
174                 cl_zalloc( sizeof(al_dev_open_context_t) );\r
175         if( !p_context )\r
176         {\r
177                 AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
178                         ("cl_malloc( %d ) failed.\n", sizeof(al_dev_open_context_t)) );\r
179                 return CL_INSUFFICIENT_MEMORY;\r
180         }\r
181 \r
182         /* Construct the open context to allow destruction. */\r
183         __construct_open_context( p_context );\r
184 \r
185         /* Initialize the open context elements. */\r
186         cl_status = __init_open_context( p_context );\r
187         if( cl_status != CL_SUCCESS )\r
188         {\r
189                 __destroy_open_context( p_context );\r
190                 return cl_status;\r
191         }\r
192 \r
193         /* Open an internal AL instance for this process. */\r
194         status = ib_open_al( &p_context->h_al );\r
195         if( status == IB_SUCCESS )\r
196         {\r
197                 /* Register for PnP events. */\r
198                 status = __proxy_reg_pnp( p_context );\r
199         }\r
200 \r
201         /* Make sure that we were able to open AL and register for PnP. */\r
202         if( status == IB_SUCCESS )\r
203         {\r
204                 /*\r
205                  * Store the reference from the AL instance back to this\r
206                  * open context.  This allows using the user-mode context\r
207                  * for resource creation.\r
208                  */\r
209                 p_context->h_al->p_context = p_context;\r
210                 /* We successfully opened the device. */\r
211                 p_io_stack->FileObject->FsContext = p_context;\r
212         }\r
213         else\r
214         {\r
215                 __destroy_open_context( p_context );\r
216                 cl_status = CL_INSUFFICIENT_RESOURCES;\r
217         }\r
218 \r
219         AL_EXIT( AL_DBG_DEV );\r
220         return cl_status;\r
221 }\r
222 \r
223 \r
224 \r
225 /*\r
226  * To be called by al_dev_open(). This will register for PnP events\r
227  * on behalf of user process (UAL). It uses the implicit global\r
228  * al instance created by AL manager. PnP events are propagated\r
229  * to UAL automatically from the time AL device is open till the\r
230  * process exits.\r
231  */\r
232 static ib_api_status_t\r
233 __proxy_reg_pnp(\r
234         IN              al_dev_open_context_t                   *p_context )\r
235 {\r
236         ib_pnp_req_t                    pnp_req;\r
237         ib_pnp_handle_t                 h_pnp;\r
238         ib_api_status_t                 status;\r
239         \r
240         /* Register for PnP events. */\r
241         cl_memclr( &pnp_req, sizeof( ib_pnp_req_t ) );\r
242         pnp_req.pnp_class = IB_PNP_CA | IB_PNP_FLAG_REG_COMPLETE;\r
243         pnp_req.pnp_context = p_context;\r
244         pnp_req.pfn_pnp_cb = proxy_pnp_ca_cb;\r
245 \r
246         /* No need to track the registration.  We'll deregister when closing AL. */\r
247         status = ib_reg_pnp( p_context->h_al, &pnp_req, &h_pnp );\r
248         if( status != IB_SUCCESS )\r
249                 return status;\r
250         \r
251         /* Register for port events. */\r
252         pnp_req.pfn_pnp_cb = proxy_pnp_port_cb;\r
253         pnp_req.pnp_class = IB_PNP_PORT | IB_PNP_FLAG_REG_COMPLETE;\r
254         status = ib_reg_pnp( p_context->h_al, &pnp_req, &h_pnp );\r
255         \r
256         return status;\r
257 }\r
258 \r
259 \r
260 \r
261 /*\r
262  * Cleanup the handle map.  Remove all mappings.  Perform all necessary\r
263  * operations.\r
264  */\r
265 static void\r
266 __proxy_cleanup_map(\r
267         IN              al_dev_open_context_t   *p_context )\r
268 {\r
269         al_handle_t                             *p_h;\r
270         size_t                                  i;\r
271 \r
272         AL_ENTER( AL_DBG_DEV );\r
273 \r
274         cl_spinlock_acquire( &p_context->h_al->obj.lock );\r
275         for( i = 0; i < cl_vector_get_size( &p_context->h_al->hdl_vector ); i++ )\r
276         {\r
277                 p_h = (al_handle_t*)\r
278                         cl_vector_get_ptr( &p_context->h_al->hdl_vector, i );\r
279 \r
280                 switch( AL_BASE_TYPE( p_h->type ) )\r
281                 {\r
282                 /* Return any MADs not reported to the user. */\r
283                 case AL_OBJ_TYPE_H_MAD:\r
284                         ib_put_mad( (ib_mad_element_t*)p_h->p_obj );\r
285                         al_hdl_free( p_context->h_al, i );\r
286                         break;\r
287 \r
288                 case AL_OBJ_TYPE_H_CA_ATTR:\r
289                         /* Release a saved CA attribute. */\r
290                         cl_free( p_h->p_obj );\r
291                         al_hdl_free( p_context->h_al, i );\r
292                         break;\r
293 \r
294                 case AL_OBJ_TYPE_H_SA_REQ:\r
295                         al_cancel_sa_req( (al_sa_req_t*)p_h->p_obj );\r
296                         break;\r
297 \r
298                 case AL_OBJ_TYPE_H_PNP_EVENT:\r
299                         cl_event_signal( &((proxy_pnp_evt_t*)p_h->p_obj)->event );\r
300                         break;\r
301 \r
302                 default:\r
303                         /* Nothing else to do for other handle types. */\r
304                         break;\r
305                 }\r
306         }\r
307         cl_spinlock_release( &p_context->h_al->obj.lock );\r
308 \r
309         AL_EXIT( AL_DBG_DEV );\r
310 }\r
311 \r
312 \r
313 cl_status_t\r
314 al_dev_close(\r
315         IN                              cl_ioctl_handle_t                       h_ioctl )\r
316 {\r
317         al_dev_open_context_t   *p_context;\r
318         IO_STACK_LOCATION               *p_io_stack;\r
319 \r
320         AL_ENTER( AL_DBG_DEV );\r
321 \r
322         p_io_stack = IoGetCurrentIrpStackLocation( h_ioctl );\r
323 \r
324         /* Determine if the client closed the al_handle. */\r
325         p_context = (al_dev_open_context_t*)p_io_stack->FileObject->FsContext;\r
326         if( !p_context )\r
327         {\r
328                 AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR,\r
329                         ("Client closed with a null open context .\n") );\r
330                 return CL_SUCCESS;\r
331         }\r
332         if( p_io_stack->FileObject->FsContext2 )\r
333         {\r
334                 /* Not the main file object - ignore. */\r
335                 AL_EXIT( AL_DBG_DEV );\r
336                 return CL_SUCCESS;\r
337         }\r
338 \r
339         /* Mark that we're closing this device. */\r
340         p_context->closing = TRUE;\r
341 \r
342         /* Flush any pending IOCTLs in case user-mode threads died on us. */\r
343         if( p_context->h_cm_ioctl )\r
344                 al_dev_cancel_ioctl( p_context->h_cm_ioctl );\r
345         if( p_context->h_comp_ioctl )\r
346                 al_dev_cancel_ioctl( p_context->h_comp_ioctl );\r
347         if( p_context->h_misc_ioctl )\r
348                 al_dev_cancel_ioctl( p_context->h_misc_ioctl );\r
349 \r
350         while( p_context->ref_cnt )\r
351         {\r
352 #ifdef _DEBUG_\r
353                 cl_status_t             cl_status;\r
354 \r
355                 cl_status = cl_event_wait_on( &p_context->close_event, 1000, FALSE );\r
356                 ASSERT( cl_status == IB_SUCCESS );\r
357                 if( cl_status != IB_SUCCESS )\r
358                 {\r
359                         AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR, ("Waiting on ref_cnt timed out!\n") );\r
360                         break;\r
361                 }\r
362 #else\r
363                 cl_event_wait_on( &p_context->close_event, EVENT_NO_TIMEOUT, FALSE );\r
364 #endif\r
365         }\r
366 \r
367         /* Cleanup any leftover callback resources. */\r
368         __proxy_cancel_cblists( p_context );\r
369 \r
370         /* Close the AL instance for this process. */\r
371         if( p_context->h_al )\r
372         {\r
373                 /* Cleanup all user to kernel handle mappings. */\r
374                 __proxy_cleanup_map( p_context );\r
375 \r
376                 ib_close_al( p_context->h_al );\r
377                 p_context->h_al = NULL;\r
378         }\r
379 \r
380         /* Destroy the open context now. */\r
381         __destroy_open_context( p_context );\r
382         cl_free( p_context );\r
383 \r
384         AL_EXIT( AL_DBG_DEV );\r
385         return CL_SUCCESS;\r
386 }\r
387 \r
388 \r
389 \r
390 /*\r
391  * Remove all callbacks on the given callback queue and return them to\r
392  * the callback pool.\r
393  */\r
394 static void\r
395 __proxy_dq_cblist(\r
396         IN              al_dev_open_context_t           *p_context,\r
397         IN              cl_qlist_t                                      *p_cblist )\r
398 {\r
399         cl_list_item_t                          *p_list_item;\r
400         al_proxy_cb_info_t                      *p_cb_info;\r
401 \r
402         cl_spinlock_acquire( &p_context->cb_lock );\r
403         for( p_list_item = cl_qlist_remove_head( p_cblist );\r
404                  p_list_item != cl_qlist_end( p_cblist );\r
405                  p_list_item = cl_qlist_remove_head( p_cblist ) )\r
406         {\r
407                 p_cb_info = (al_proxy_cb_info_t*)p_list_item;\r
408                 if( p_cb_info->p_al_obj )\r
409                         deref_al_obj( p_cb_info->p_al_obj );\r
410                 proxy_cb_put( p_cb_info );\r
411         }\r
412         cl_spinlock_release( &p_context->cb_lock );\r
413 }\r
414 \r
415 \r
416 \r
417 /*\r
418  * Remove all queued callbacks from all callback lists.\r
419  */\r
420 static void\r
421 __proxy_cancel_cblists(\r
422         IN              al_dev_open_context_t                   *p_context )\r
423 {\r
424         __proxy_dq_cblist( p_context, &p_context->cm_cb_list );\r
425         __proxy_dq_cblist( p_context, &p_context->comp_cb_list );\r
426         __proxy_dq_cblist( p_context, &p_context->misc_cb_list );\r
427 }\r
428 \r
429 \r
430 cl_status_t\r
431 al_dev_ioctl(\r
432         IN                              cl_ioctl_handle_t                       h_ioctl )\r
433 {\r
434         cl_status_t                     cl_status;\r
435         size_t                          ret_bytes = 0;\r
436         void                            *p_open_context;\r
437         IO_STACK_LOCATION       *p_io_stack;\r
438 \r
439         AL_ENTER( AL_DBG_DEV );\r
440 \r
441         p_io_stack = IoGetCurrentIrpStackLocation( h_ioctl );\r
442         p_open_context = p_io_stack->FileObject->FsContext;\r
443 \r
444         AL_PRINT( TRACE_LEVEL_INFORMATION, AL_DBG_DEV,\r
445                 ("al_dev_ioctl: buf_size (%d) p_buf (%016I64x).\n",\r
446                 cl_ioctl_in_size( h_ioctl ), (LONG_PTR)cl_ioctl_in_buf( h_ioctl )) );\r
447 \r
448         /* Process the ioctl command. */\r
449         if( IS_AL_PROXY_IOCTL(cl_ioctl_ctl_code( h_ioctl )) )\r
450                 cl_status = proxy_ioctl( h_ioctl, &ret_bytes );\r
451         else if( IS_VERBS_IOCTL(cl_ioctl_ctl_code( h_ioctl )) )\r
452                 cl_status = verbs_ioctl( h_ioctl, &ret_bytes );\r
453         //else if( IS_CM_IOCTL(cl_ioctl_ctl_code( h_ioctl )) )\r
454         //      cl_status = cm_ioctl( h_ioctl, &ret_bytes );\r
455         else if( IS_CEP_IOCTL(cl_ioctl_ctl_code( h_ioctl )) )\r
456                 cl_status = cep_ioctl( h_ioctl, &ret_bytes );\r
457         else if( IS_AL_IOCTL(cl_ioctl_ctl_code( h_ioctl )) )\r
458                 cl_status = al_ioctl( h_ioctl, &ret_bytes );\r
459         else if( IS_SUBNET_IOCTL(cl_ioctl_ctl_code( h_ioctl )) )\r
460                 cl_status = subnet_ioctl( h_ioctl, &ret_bytes );\r
461         else if( IS_IOC_IOCTL(cl_ioctl_ctl_code( h_ioctl )) )\r
462                 cl_status = ioc_ioctl( h_ioctl, &ret_bytes );\r
463         else\r
464                 cl_status = CL_INVALID_REQUEST;\r
465 \r
466         switch( cl_status )\r
467         {\r
468         case CL_COMPLETED:\r
469                 /* Flip the status since the IOCTL was completed. */\r
470                 cl_status = CL_SUCCESS;\r
471                 /* Fall through */\r
472         case CL_PENDING:\r
473                 break;\r
474         case CL_INVALID_REQUEST:\r
475                 /*\r
476                  * In Windows, Driver Verifier sends bogus IOCTLs to the device.\r
477                  * These must be passed down the device stack, and so cannot be\r
478                  * completed in the IOCTL handler.  They are properly cleaned up,\r
479                  * though no data is returned to the user.\r
480                  */\r
481                 break;\r
482         default:\r
483                 cl_ioctl_complete( h_ioctl, cl_status, ret_bytes );\r
484         }\r
485 \r
486         AL_EXIT( AL_DBG_DEV );\r
487         return cl_status;\r
488 }\r
489 \r
490 \r
491 \r
492 /*\r
493  * Cancel any pending IOCTL calls for the specified type.\r
494  * This routine is also called when closing the device.\r
495  */\r
496 void\r
497 al_dev_cancel_ioctl(\r
498         IN                              cl_ioctl_handle_t                       h_ioctl )\r
499 {\r
500         al_dev_open_context_t   *p_context;\r
501         cl_ioctl_handle_t               *ph_ioctl;\r
502         PIO_STACK_LOCATION              p_io_stack;\r
503 \r
504         /*\r
505          * Search the ioctl buffer in the process specific queue\r
506          * Dequeue it, if found\r
507          */\r
508         AL_ENTER( AL_DBG_DEV );\r
509 \r
510         /* Get the stack location. */\r
511         p_io_stack = IoGetCurrentIrpStackLocation( h_ioctl );\r
512 \r
513         p_context = (al_dev_open_context_t *)p_io_stack->FileObject->FsContext;\r
514         ASSERT( p_context );\r
515 \r
516         /* Clear the IOCTL. */\r
517         cl_spinlock_acquire( &p_context->cb_lock );\r
518         switch( cl_ioctl_ctl_code( h_ioctl ) )\r
519         {\r
520         case UAL_GET_CM_CB_INFO:\r
521                 ph_ioctl = &p_context->h_cm_ioctl;\r
522                 break;\r
523         case UAL_GET_COMP_CB_INFO:\r
524                 ph_ioctl = &p_context->h_comp_ioctl;\r
525                 break;\r
526         case UAL_GET_MISC_CB_INFO:\r
527                 ph_ioctl = &p_context->h_misc_ioctl;\r
528                 break;\r
529         default:\r
530                 AL_PRINT( TRACE_LEVEL_ERROR, AL_DBG_ERROR, ("Invalid CB type\n") );\r
531                 ph_ioctl = NULL;\r
532                 break;\r
533         }\r
534 \r
535         if( ph_ioctl && *ph_ioctl == h_ioctl )\r
536         {\r
537                 *ph_ioctl = NULL;\r
538 #pragma warning(push, 3)\r
539                 IoSetCancelRoutine( h_ioctl, NULL );\r
540 #pragma warning(pop)\r
541 \r
542                 /* Complete the IOCTL. */\r
543                 cl_ioctl_complete( h_ioctl, CL_CANCELED, 0 );\r
544                 proxy_context_deref( p_context );\r
545         }\r
546         cl_spinlock_release( &p_context->cb_lock );\r
547 \r
548         AL_EXIT( AL_DBG_DEV );\r
549 }\r
550 \r
551 \r
552 void\r
553 al_dev_cancel_io(\r
554         IN                              DEVICE_OBJECT                           *p_dev_obj,\r
555         IN                              IRP                                                     *p_irp )\r
556 {\r
557         AL_ENTER( AL_DBG_DEV );\r
558 \r
559         UNUSED_PARAM( p_dev_obj );\r
560 \r
561         al_dev_cancel_ioctl( p_irp );\r
562 \r
563         IoReleaseCancelSpinLock( p_irp->CancelIrql );\r
564 \r
565         AL_EXIT( AL_DBG_DEV );\r
566 }\r