xref: /illumos-gate/usr/src/uts/common/io/ib/mgt/ibdma/ibdma.c (revision 7ab4e62e3b5c454f248a38bec0d489e8f5543324)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
24  */
25 
26 /*
27  * Infiniband Device Management Agent for IB storage.
28  */
29 
30 #include <sys/conf.h>
31 #include <sys/file.h>
32 #include <sys/ddi.h>
33 #include <sys/sunddi.h>
34 #include <sys/modctl.h>
35 #include <sys/priv.h>
36 #include <sys/sysmacros.h>
37 
38 #include <sys/ib/ibtl/ibti.h>		/* IB public interfaces */
39 
40 #include <sys/ib/mgt/ibdma/ibdma.h>
41 #include <sys/ib/mgt/ibdma/ibdma_impl.h>
42 
43 /*
44  * NOTE: The IB Device Management Agent function, like other IB
45  * managers and agents is best implemented as a kernel misc.
46  * module.
47  * Eventually we could modify IBT_DM_AGENT so that we don't need to
48  * open each HCA to receive asynchronous events.
49  */
50 
51 #define	IBDMA_NAME_VERSION	"IB Device Management Agent"
52 
53 extern struct mod_ops mod_miscops;
54 
55 static void ibdma_ibt_async_handler(void *clnt, ibt_hca_hdl_t hdl,
56 	ibt_async_code_t code, ibt_async_event_t *event);
57 
58 static void ibdma_mad_recv_cb(ibmf_handle_t ibmf_hdl,
59 	ibmf_msg_t *msgp, void *args);
60 static void ibdma_create_resp_mad(ibmf_msg_t *msgp);
61 
62 /*
63  * Misc. kernel module for now.
64  */
65 static struct modlmisc modlmisc = {
66 	&mod_miscops,
67 	IBDMA_NAME_VERSION
68 };
69 
70 static struct modlinkage modlinkage = {
71 	MODREV_1, (void *)&modlmisc, NULL
72 };
73 
74 static ibt_clnt_modinfo_t ibdma_ibt_modinfo = {
75 	IBTI_V_CURR,
76 	IBT_DM_AGENT,
77 	ibdma_ibt_async_handler,
78 	NULL,
79 	"ibdma"
80 };
81 
82 /*
83  * Module global state allocated at init().
84  */
85 static ibdma_mod_state_t	*ibdma = NULL;
86 
87 /*
88  * Init/Fini handlers and IBTL HCA management prototypes.
89  */
90 static int ibdma_init();
91 static int ibdma_fini();
92 static int ibdma_ibt_init();
93 static void ibdma_ibt_fini();
94 static ibdma_hca_t *ibdma_hca_init(ib_guid_t guid);
95 static void ibdma_hca_fini(ibdma_hca_t *hca);
96 static ibdma_hca_t *ibdma_find_hca(ib_guid_t guid);
97 
98 /*
99  * DevMgmt Agent MAD attribute handlers prototypes.
100  */
101 static void ibdma_get_class_portinfo(ibmf_msg_t *msg);
102 static void ibdma_get_io_unitinfo(ibdma_hca_t *hca, ibmf_msg_t *msg);
103 static void ibdma_get_ioc_profile(ibdma_hca_t *hca, ibmf_msg_t *msg);
104 static void ibdma_get_ioc_services(ibdma_hca_t *hca, ibmf_msg_t *msg);
105 
106 /*
107  * _init()
108  */
109 int
110 _init(void)
111 {
112 	int status;
113 
114 	ASSERT(ibdma == NULL);
115 
116 	ibdma = kmem_zalloc(sizeof (*ibdma), KM_SLEEP);
117 	ASSERT(ibdma != NULL);
118 
119 	status = ibdma_init();
120 	if (status != DDI_SUCCESS) {
121 		kmem_free(ibdma, sizeof (*ibdma));
122 		ibdma = NULL;
123 		return (status);
124 	}
125 
126 	status = mod_install(&modlinkage);
127 	if (status != DDI_SUCCESS) {
128 		cmn_err(CE_NOTE, "_init, mod_install error (%d)", status);
129 		(void) ibdma_fini();
130 		kmem_free(ibdma, sizeof (*ibdma));
131 		ibdma = NULL;
132 	}
133 	return (status);
134 }
135 
136 /*
137  * _info()
138  */
139 int
140 _info(struct modinfo *modinfop)
141 {
142 	return (mod_info(&modlinkage, modinfop));
143 }
144 
145 /*
146  * _fini()
147  */
148 int
149 _fini(void)
150 {
151 	int		status;
152 	int		slot;
153 	ibdma_hca_t	*hca;
154 
155 	status = mod_remove(&modlinkage);
156 	if (status != DDI_SUCCESS) {
157 		cmn_err(CE_NOTE, "_fini, mod_remove error (%d)", status);
158 		return (status);
159 	}
160 
161 	/*
162 	 * Sanity check to see if anyone is not cleaning
163 	 * up appropriately.
164 	 */
165 	mutex_enter(&ibdma->ms_hca_list_lock);
166 	hca = list_head(&ibdma->ms_hca_list);
167 	while (hca != NULL) {
168 		for (slot = 0; slot < IBDMA_MAX_IOC; slot++) {
169 			if (hca->ih_ioc[slot].ii_inuse) {
170 				cmn_err(CE_NOTE, "_fini, IOC %d still attached"
171 				    " for (0x%0llx)", slot+1,
172 				    (u_longlong_t)hca->ih_iou_guid);
173 			}
174 		}
175 		hca = list_next(&ibdma->ms_hca_list, hca);
176 	}
177 	mutex_exit(&ibdma->ms_hca_list_lock);
178 
179 	(void) ibdma_fini();
180 	kmem_free(ibdma, sizeof (*ibdma));
181 	return (status);
182 }
183 
184 /*
185  * ibdma_init()
186  *
187  * Initialize I/O Unit structure, generate initial HCA list and register
188  * it port with the IBMF.
189  */
190 static int
191 ibdma_init()
192 {
193 	int		status;
194 
195 	/*
196 	 * Global lock and I/O Unit initialization.
197 	 */
198 	mutex_init(&ibdma->ms_hca_list_lock, NULL, MUTEX_DRIVER, NULL);
199 
200 	/*
201 	 * Discover IB hardware and setup for device management agent
202 	 * support.
203 	 */
204 	status = ibdma_ibt_init();
205 	if (status != DDI_SUCCESS) {
206 		cmn_err(CE_NOTE, "ibdma_init, ibt_attach failed (%d)",
207 		    status);
208 		mutex_destroy(&ibdma->ms_hca_list_lock);
209 		return (status);
210 	}
211 
212 	return (status);
213 }
214 
215 /*
216  * ibdma_fini()
217  *
218  * Release resource if we are no longer in use.
219  */
220 static int
221 ibdma_fini()
222 {
223 	ibdma_ibt_fini();
224 	mutex_destroy(&ibdma->ms_hca_list_lock);
225 	return (DDI_SUCCESS);
226 }
227 
228 /*
229  * ibdma_ibt_async_handler()
230  */
231 /* ARGSUSED */
232 static void
233 ibdma_ibt_async_handler(void *clnt, ibt_hca_hdl_t hdl,
234 	ibt_async_code_t code, ibt_async_event_t *event)
235 {
236 	ibdma_hca_t	*hca;
237 
238 	switch (code) {
239 
240 	case IBT_EVENT_PORT_UP:
241 	case IBT_ERROR_PORT_DOWN:
242 	case IBT_PORT_CHANGE_EVENT:
243 	case IBT_CLNT_REREG_EVENT:
244 		break;
245 
246 	case IBT_HCA_ATTACH_EVENT:
247 		mutex_enter(&ibdma->ms_hca_list_lock);
248 		hca = ibdma_hca_init(event->ev_hca_guid);
249 		if (hca != NULL) {
250 			list_insert_tail(&ibdma->ms_hca_list, hca);
251 			cmn_err(CE_NOTE, "hca ibt hdl (%p)",
252 			    (void *)hca->ih_ibt_hdl);
253 			ibdma->ms_num_hcas++;
254 		}
255 		mutex_exit(&ibdma->ms_hca_list_lock);
256 		break;
257 
258 	case IBT_HCA_DETACH_EVENT:
259 		mutex_enter(&ibdma->ms_hca_list_lock);
260 		hca = ibdma_find_hca(event->ev_hca_guid);
261 		if (hca != NULL) {
262 			list_remove(&ibdma->ms_hca_list, hca);
263 			cmn_err(CE_NOTE, "removing hca (%p) (0x%llx)",
264 			    (void *)hca, hca ?
265 			    (u_longlong_t)hca->ih_iou_guid : 0x0ll);
266 			ibdma_hca_fini(hca);
267 		}
268 		mutex_exit(&ibdma->ms_hca_list_lock);
269 		break;
270 
271 	default:
272 #ifdef	DEBUG
273 		cmn_err(CE_NOTE, "ibt_async_handler, unhandled event(%d)",
274 		    code);
275 #endif
276 		break;
277 	}
278 
279 }
280 
281 /*
282  * ibdma_ibt_init()
283  */
284 static int
285 ibdma_ibt_init()
286 {
287 	int		status;
288 	int		hca_cnt;
289 	int		hca_ndx;
290 	ib_guid_t	*guid;
291 	ibdma_hca_t	*hca;
292 
293 	/*
294 	 * Attach to IBTF and get HCA list.
295 	 */
296 	status = ibt_attach(&ibdma_ibt_modinfo, NULL,
297 	    ibdma, &ibdma->ms_ibt_hdl);
298 	if (status != DDI_SUCCESS) {
299 		cmn_err(CE_NOTE, "ibt_init, ibt_attach failed (%d)",
300 		    status);
301 		return (status);
302 	}
303 
304 	list_create(&ibdma->ms_hca_list, sizeof (ibdma_hca_t),
305 	    offsetof(ibdma_hca_t, ih_node));
306 
307 	hca_cnt = ibt_get_hca_list(&guid);
308 	if (hca_cnt < 1) {
309 #ifdef	DEBUG_IBDMA
310 		cmn_err(CE_NOTE, "ibt_init, no HCA(s) found");
311 #endif
312 		/* not an error if no HCAs, but nothing more to do here */
313 		return (DDI_SUCCESS);
314 	}
315 
316 	mutex_enter(&ibdma->ms_hca_list_lock);
317 
318 	for (hca_ndx = 0; hca_ndx < hca_cnt; hca_ndx++) {
319 #ifdef	DEBUG_IBDMA
320 		cmn_err(CE_NOTE, "adding hca GUID(0x%llx)",
321 		    (u_longlong_t)guid[hca_ndx]);
322 #endif
323 
324 		hca = ibdma_hca_init(guid[hca_ndx]);
325 		if (hca == NULL) {
326 			cmn_err(CE_NOTE, "ibt_init, hca_init GUID(0x%llx)"
327 			    " failed", (u_longlong_t)guid[hca_ndx]);
328 			continue;
329 		}
330 		list_insert_tail(&ibdma->ms_hca_list, hca);
331 		ibdma->ms_num_hcas++;
332 	}
333 
334 	mutex_exit(&ibdma->ms_hca_list_lock);
335 
336 	ibt_free_hca_list(guid, hca_cnt);
337 #ifdef	DEBUG_IBDMA
338 	cmn_err(CE_NOTE, "Added %d HCA(s)",
339 	    ibdma->ms_num_hcas);
340 #endif
341 	return (DDI_SUCCESS);
342 }
343 
344 /*
345  * ibdma_ibt_fini()
346  */
347 static void
348 ibdma_ibt_fini()
349 {
350 	ibdma_hca_t		*hca;
351 	ibdma_hca_t		*next;
352 
353 	mutex_enter(&ibdma->ms_hca_list_lock);
354 	hca = list_head(&ibdma->ms_hca_list);
355 	while (hca != NULL) {
356 		next = list_next(&ibdma->ms_hca_list, hca);
357 		list_remove(&ibdma->ms_hca_list, hca);
358 #ifdef	DEBUG_IBDMA
359 		cmn_err(CE_NOTE, "removing hca (%p) (0x%llx)",
360 		    (void *)hca, hca ?
361 		    (u_longlong_t)hca->ih_iou_guid : 0x0ll);
362 		cmn_err(CE_NOTE, "hca ibt hdl (%p)",
363 		    (void *)hca->ih_ibt_hdl);
364 #endif
365 		ibdma_hca_fini(hca);
366 		hca = next;
367 	}
368 	list_destroy(&ibdma->ms_hca_list);
369 
370 	(void) ibt_detach(ibdma->ms_ibt_hdl);
371 	ibdma->ms_ibt_hdl   = NULL;
372 	ibdma->ms_num_hcas  = 0;
373 	mutex_exit(&ibdma->ms_hca_list_lock);
374 }
375 
376 /*
377  * ibdma_find_hca()
378  */
379 static ibdma_hca_t *
380 ibdma_find_hca(ib_guid_t guid)
381 {
382 	ibdma_hca_t	*hca;
383 
384 	ASSERT(mutex_owned(&ibdma->ms_hca_list_lock));
385 
386 	hca = list_head(&ibdma->ms_hca_list);
387 	while (hca != NULL) {
388 		if (hca->ih_iou_guid == guid) {
389 			break;
390 		}
391 		hca = list_next(&ibdma->ms_hca_list, hca);
392 	}
393 	return (hca);
394 }
395 
396 /*
397  * ibdma_hca_init()
398  */
399 static ibdma_hca_t *
400 ibdma_hca_init(ib_guid_t guid)
401 {
402 	ibt_status_t		status;
403 	ibdma_hca_t		*hca;
404 	ibdma_port_t		*port;
405 	ibt_hca_attr_t		hca_attr;
406 	int			ndx;
407 
408 	ASSERT(mutex_owned(&ibdma->ms_hca_list_lock));
409 
410 	status = ibt_query_hca_byguid(guid, &hca_attr);
411 	if (status != IBT_SUCCESS) {
412 		cmn_err(CE_NOTE, "hca_init HCA query error (%d)",
413 		    status);
414 		return (NULL);
415 	}
416 
417 	if (ibdma_find_hca(guid) != NULL) {
418 #ifdef	DEBUG_IBDMA
419 		cmn_err(CE_NOTE, "hca_init HCA already exists");
420 #endif
421 		return (NULL);
422 	}
423 
424 	hca = kmem_zalloc(sizeof (ibdma_hca_t) +
425 	    (hca_attr.hca_nports-1)*sizeof (ibdma_port_t), KM_SLEEP);
426 	ASSERT(hca != NULL);
427 
428 	hca->ih_nports   = hca_attr.hca_nports;
429 
430 	rw_init(&hca->ih_iou_rwlock, NULL, RW_DRIVER, NULL);
431 	rw_enter(&hca->ih_iou_rwlock, RW_WRITER);
432 	hca->ih_iou_guid		= guid;
433 	hca->ih_iou.iou_changeid	= h2b16(1);
434 	hca->ih_iou.iou_num_ctrl_slots	= IBDMA_MAX_IOC;
435 	hca->ih_iou.iou_flag		= IB_DM_IOU_OPTIONROM_ABSENT;
436 
437 	list_create(&hca->ih_hdl_list, sizeof (ibdma_hdl_impl_t),
438 	    offsetof(ibdma_hdl_impl_t, ih_node));
439 	rw_exit(&hca->ih_iou_rwlock);
440 
441 	/*
442 	 * It would be better to not open, but IBTL is setup to only allow
443 	 * certain managers to get async call backs if not open.
444 	 */
445 	status = ibt_open_hca(ibdma->ms_ibt_hdl, guid, &hca->ih_ibt_hdl);
446 	if (status != IBT_SUCCESS) {
447 		cmn_err(CE_NOTE, "hca_init() IBT open failed (%d)",
448 		    status);
449 
450 		list_destroy(&hca->ih_hdl_list);
451 		rw_destroy(&hca->ih_iou_rwlock);
452 		kmem_free(hca, sizeof (ibdma_hca_t) +
453 		    (hca_attr.hca_nports-1)*sizeof (ibdma_port_t));
454 		return (NULL);
455 	}
456 
457 	/*
458 	 * Register with the IB Management Framework and setup MAD call-back.
459 	 */
460 	for (ndx = 0; ndx < hca->ih_nports; ndx++) {
461 		port = &hca->ih_port[ndx];
462 		port->ip_hcap = hca;
463 		port->ip_ibmf_reg.ir_ci_guid	= hca->ih_iou_guid;
464 		port->ip_ibmf_reg.ir_port_num	= ndx + 1;
465 		port->ip_ibmf_reg.ir_client_class = DEV_MGT_AGENT;
466 
467 		status = ibmf_register(&port->ip_ibmf_reg, IBMF_VERSION,
468 		    0, NULL, NULL, &port->ip_ibmf_hdl, &port->ip_ibmf_caps);
469 		if (status != IBMF_SUCCESS) {
470 			cmn_err(CE_NOTE, "hca_init, IBMF register failed (%d)",
471 			    status);
472 			port->ip_ibmf_hdl = NULL;
473 			ibdma_hca_fini(hca);
474 			return (NULL);
475 		}
476 
477 		status = ibmf_setup_async_cb(port->ip_ibmf_hdl,
478 		    IBMF_QP_HANDLE_DEFAULT, ibdma_mad_recv_cb, port, 0);
479 		if (status != IBMF_SUCCESS) {
480 			cmn_err(CE_NOTE, "hca_init, IBMF cb setup failed (%d)",
481 			    status);
482 			ibdma_hca_fini(hca);
483 			return (NULL);
484 		}
485 
486 		status = ibt_modify_port_byguid(hca->ih_iou_guid,
487 		    ndx+1, IBT_PORT_SET_DEVMGT, 0);
488 		if (status != IBT_SUCCESS) {
489 			cmn_err(CE_NOTE, "hca_init, IBT modify port caps"
490 			    " error (%d)", status);
491 			ibdma_hca_fini(hca);
492 			return (NULL);
493 		}
494 	}
495 	return (hca);
496 }
497 
498 /*
499  * ibdma_hca_fini()
500  */
501 static void
502 ibdma_hca_fini(ibdma_hca_t *hca)
503 {
504 	int			status;
505 	int			ndx;
506 	ibdma_port_t		*port;
507 	ibdma_hdl_impl_t	*hdl;
508 	ibdma_hdl_impl_t	*hdl_next;
509 
510 	ASSERT(mutex_owned(&ibdma->ms_hca_list_lock));
511 	ASSERT(hca != NULL);
512 
513 	rw_enter(&hca->ih_iou_rwlock, RW_WRITER);
514 
515 	/*
516 	 * All handles should have been de-registered, but release
517 	 * any that are outstanding.
518 	 */
519 	hdl = list_head(&hca->ih_hdl_list);
520 	while (hdl != NULL) {
521 		hdl_next = list_next(&hca->ih_hdl_list, hdl);
522 		list_remove(&hca->ih_hdl_list, hdl);
523 		cmn_err(CE_NOTE, "hca_fini, unexpected ibdma user handle"
524 		    " exists");
525 		kmem_free(hdl, sizeof (*hdl));
526 		hdl = hdl_next;
527 	}
528 	list_destroy(&hca->ih_hdl_list);
529 
530 	/*
531 	 * Un-register with the IBMF.
532 	 */
533 	for (ndx = 0; ndx < hca->ih_nports; ndx++) {
534 		port = &hca->ih_port[ndx];
535 		port->ip_hcap = NULL;
536 
537 		status = ibt_modify_port_byguid(hca->ih_iou_guid,
538 		    ndx+1, IBT_PORT_RESET_DEVMGT, 0);
539 		if (status != IBT_SUCCESS)
540 			cmn_err(CE_NOTE, "hca_fini, IBT modify port caps"
541 			    " error (%d)", status);
542 
543 		if (port->ip_ibmf_hdl == NULL)
544 			continue;
545 
546 		status = ibmf_tear_down_async_cb(port->ip_ibmf_hdl,
547 		    IBMF_QP_HANDLE_DEFAULT, 0);
548 		if (status != IBMF_SUCCESS)
549 			cmn_err(CE_NOTE, "hca_fini, IBMF tear down cb"
550 			    " error (%d)", status);
551 
552 		status = ibmf_unregister(&port->ip_ibmf_hdl, 0);
553 		if (status != IBMF_SUCCESS)
554 			cmn_err(CE_NOTE, "hca_fini, IBMF un-register"
555 			    " error (%d)", status);
556 		port->ip_ibmf_hdl = NULL;
557 	}
558 
559 	status = ibt_close_hca(hca->ih_ibt_hdl);
560 	if (status != IBT_SUCCESS)
561 		cmn_err(CE_NOTE, "hca_fini close error (%d)", status);
562 
563 	rw_exit(&hca->ih_iou_rwlock);
564 	rw_destroy(&hca->ih_iou_rwlock);
565 	kmem_free(hca, sizeof (ibdma_hca_t) +
566 	    (hca->ih_nports-1) * sizeof (ibdma_port_t));
567 }
568 
569 /* DM IBMF MAD handlers */
570 /*
571  * ibdma_create_resp_mad()
572  */
573 static void
574 ibdma_create_resp_mad(ibmf_msg_t *msgp)
575 {
576 	/*
577 	 * Allocate send buffer fix up hdr for response.
578 	 */
579 	msgp->im_msgbufs_send.im_bufs_mad_hdr =
580 	    kmem_zalloc(IBDMA_MAD_SIZE, KM_SLEEP);
581 
582 	msgp->im_msgbufs_send.im_bufs_cl_hdr = (uchar_t *)
583 	    msgp->im_msgbufs_send.im_bufs_mad_hdr + sizeof (ib_mad_hdr_t);
584 	msgp->im_msgbufs_send.im_bufs_cl_hdr_len = IBDMA_DM_MAD_HDR_SIZE;
585 	msgp->im_msgbufs_send.im_bufs_cl_data =
586 	    ((char *)msgp->im_msgbufs_send.im_bufs_cl_hdr +
587 	    IBDMA_DM_MAD_HDR_SIZE);
588 	msgp->im_msgbufs_send.im_bufs_cl_data_len =
589 	    IBDMA_MAD_SIZE - sizeof (ib_mad_hdr_t) - IBDMA_DM_MAD_HDR_SIZE;
590 	(void) memcpy(msgp->im_msgbufs_send.im_bufs_mad_hdr,
591 	    msgp->im_msgbufs_recv.im_bufs_mad_hdr, IBDMA_MAD_SIZE);
592 
593 	/*
594 	 * We may want to support a GRH since this is a GMP; not
595 	 * required for current SRP device manager platforms.
596 	 */
597 #if 0
598 	if (msgp->im_msg_flags & IBMF_MSG_FLAGS_GLOBAL_ADDRESS) {
599 		ib_gid_t	temp = msgp->im_global_addr.ig_recver_gid;
600 
601 		msgp->im_global_addr.ig_recver_gid =
602 		    msgp->im_global_addr.ig_sender_gid;
603 		msgp->im_global_addr.ig_sender_gid = temp;
604 	}
605 #endif
606 }
607 
608 /*
609  * ibdma_mad_send_cb()
610  */
611 /* ARGSUSED */
612 static void
613 ibdma_mad_send_cb(ibmf_handle_t ibmf_hdl, ibmf_msg_t *msgp, void *arg)
614 {
615 	/*
616 	 * Just free the buffers and release the message.
617 	 */
618 	if (msgp->im_msgbufs_send.im_bufs_mad_hdr != NULL) {
619 		kmem_free(msgp->im_msgbufs_send.im_bufs_mad_hdr,
620 		    IBDMA_MAD_SIZE);
621 		msgp->im_msgbufs_send.im_bufs_mad_hdr = NULL;
622 	}
623 	if (ibmf_free_msg(ibmf_hdl, &msgp) != IBMF_SUCCESS) {
624 		cmn_err(CE_NOTE, "mad_send_cb, IBMF message free error");
625 	}
626 }
627 
628 /*
629  * ibdma_mad_recv_cb()
630  */
631 static void
632 ibdma_mad_recv_cb(ibmf_handle_t ibmf_hdl, ibmf_msg_t *msgp, void *args)
633 {
634 	int		status;
635 	ib_mad_hdr_t	*in_mad;
636 	ib_mad_hdr_t	*out_mad;
637 	ibdma_port_t	*port = args;
638 
639 	ASSERT(msgp != NULL);
640 	ASSERT(port != NULL);
641 
642 	if (msgp->im_msg_status != IBMF_SUCCESS) {
643 		cmn_err(CE_NOTE, "mad_recv_cb, bad MAD receive status (%d)",
644 		    msgp->im_msg_status);
645 		goto drop;
646 	}
647 
648 	in_mad = msgp->im_msgbufs_recv.im_bufs_mad_hdr;
649 
650 	if (in_mad->MgmtClass != MAD_MGMT_CLASS_DEV_MGT) {
651 #ifdef	DEBUG_IBDMA
652 		cmn_err(CE_NOTE, "mad_recv_cb, MAD not of Dev Mgmt Class");
653 #endif
654 		goto drop;
655 	}
656 
657 	ibdma_create_resp_mad(msgp);
658 	out_mad = msgp->im_msgbufs_send.im_bufs_mad_hdr;
659 
660 	out_mad->R_Method = IB_DM_DEVMGT_METHOD_GET_RESP;
661 	out_mad->Status   = 0;
662 
663 	if (in_mad->R_Method == MAD_METHOD_SET) {
664 #ifdef	DEBUG_IBDMA
665 		cmn_err(CE_NOTE, "mad_recv_cb, no attributes supported"
666 		    " for set");
667 #endif
668 		out_mad->Status = MAD_STATUS_UNSUPP_METHOD_ATTR;
669 		goto send_resp;
670 	}
671 
672 	if (in_mad->R_Method != MAD_METHOD_GET) {
673 #ifdef	DEBUG_IBDMA
674 		cmn_err(CE_NOTE, "mad_recv_cb, no attributes supported"
675 		    " for set");
676 #endif
677 		out_mad->Status = MAD_STATUS_UNSUPP_METHOD;
678 		goto send_resp;
679 	}
680 
681 	/*
682 	 * Process a GET method.
683 	 */
684 	switch (b2h16(in_mad->AttributeID)) {
685 
686 	case IB_DM_ATTR_CLASSPORTINFO:
687 		ibdma_get_class_portinfo(msgp);
688 		break;
689 
690 	case IB_DM_ATTR_IO_UNITINFO:
691 		ibdma_get_io_unitinfo(port->ip_hcap, msgp);
692 		break;
693 
694 	case IB_DM_ATTR_IOC_CTRL_PROFILE:
695 		ibdma_get_ioc_profile(port->ip_hcap, msgp);
696 		break;
697 
698 	case IB_DM_ATTR_SERVICE_ENTRIES:
699 		ibdma_get_ioc_services(port->ip_hcap, msgp);
700 		break;
701 
702 	default:
703 		out_mad->Status = MAD_STATUS_UNSUPP_METHOD_ATTR;
704 		break;
705 	}
706 
707 send_resp:
708 	status = ibmf_msg_transport(ibmf_hdl, IBMF_QP_HANDLE_DEFAULT,
709 	    msgp, NULL, ibdma_mad_send_cb, NULL, 0);
710 	if (status != IBMF_SUCCESS) {
711 		cmn_err(CE_NOTE, "mad_recv_cb, send error (%d)", status);
712 		ibdma_mad_send_cb(ibmf_hdl, msgp, NULL);
713 	}
714 	return;
715 
716 drop:
717 	status = ibmf_free_msg(ibmf_hdl, &msgp);
718 	if (status != IBMF_SUCCESS) {
719 		cmn_err(CE_NOTE, "mad_recv_cb, error dropping (%d)",
720 		    status);
721 	}
722 }
723 
724 /*
725  * ibdma_get_class_portinfo()
726  */
727 static void
728 ibdma_get_class_portinfo(ibmf_msg_t *msg)
729 {
730 	ib_mad_classportinfo_t	*cpip;
731 
732 	cpip = (ib_mad_classportinfo_t *)msg->im_msgbufs_send.im_bufs_cl_data;
733 	bzero(cpip, sizeof (*cpip));
734 	cpip->BaseVersion   = MAD_CLASS_BASE_VERS_1;
735 	cpip->ClassVersion  = IB_DM_CLASS_VERSION_1;
736 	cpip->RespTimeValue = h2b32(IBDMA_DM_RESP_TIME);
737 }
738 
739 /*
740  * ibdma_get_io_unitinfo()
741  */
742 static void
743 ibdma_get_io_unitinfo(ibdma_hca_t *hca, ibmf_msg_t *msg)
744 {
745 	ib_dm_io_unitinfo_t	*uip;
746 
747 	uip = (ib_dm_io_unitinfo_t *)msg->im_msgbufs_send.im_bufs_cl_data;
748 	rw_enter(&hca->ih_iou_rwlock, RW_READER);
749 	bcopy(&hca->ih_iou, uip, sizeof (ib_dm_io_unitinfo_t));
750 	rw_exit(&hca->ih_iou_rwlock);
751 }
752 
753 /*
754  * ibdma_get_ioc_profile()
755  */
756 static void
757 ibdma_get_ioc_profile(ibdma_hca_t *hca, ibmf_msg_t *msg)
758 {
759 	ib_dm_ioc_ctrl_profile_t	*iocp;
760 	uint32_t			slot;
761 
762 	ASSERT(msg != NULL);
763 
764 	slot = b2h32(msg->im_msgbufs_recv.im_bufs_mad_hdr->AttributeModifier);
765 	iocp = (ib_dm_ioc_ctrl_profile_t *)
766 	    msg->im_msgbufs_send.im_bufs_cl_data;
767 	if (slot == 0 || slot > IBDMA_MAX_IOC) {
768 		msg->im_msgbufs_send.im_bufs_mad_hdr->Status =
769 		    MAD_STATUS_INVALID_FIELD;
770 		return;
771 	}
772 
773 	slot--;
774 	rw_enter(&hca->ih_iou_rwlock, RW_READER);
775 	if (ibdma_get_ioc_state(hca, slot) == IBDMA_IOC_PRESENT) {
776 		bcopy(&hca->ih_ioc[slot].ii_profile, iocp,
777 		    sizeof (ib_dm_ioc_ctrl_profile_t));
778 	} else {
779 		msg->im_msgbufs_send.im_bufs_mad_hdr->Status =
780 		    IB_DM_DEVMGT_MAD_STAT_NORESP;
781 	}
782 	rw_exit(&hca->ih_iou_rwlock);
783 }
784 
785 /*
786  * ibdma_get_ioc_services()
787  */
788 static void
789 ibdma_get_ioc_services(ibdma_hca_t *hca, ibmf_msg_t *msg)
790 {
791 	ib_dm_srv_t	*to_svcp;
792 	ib_dm_srv_t	*from_svcp;
793 	uint32_t	slot;
794 	uint8_t		hi;
795 	uint8_t		low;
796 
797 	ASSERT(msg != NULL);
798 
799 	slot = b2h32(msg->im_msgbufs_recv.im_bufs_mad_hdr->AttributeModifier);
800 	hi   = (slot >> 8) & 0x00FF;
801 	low  = slot  & 0x00FF;
802 	slot = (slot >> 16) & 0x0FFFF;
803 	if (slot == 0 || slot > IBDMA_MAX_IOC) {
804 		msg->im_msgbufs_send.im_bufs_mad_hdr->Status =
805 		    MAD_STATUS_INVALID_FIELD;
806 		return;
807 	}
808 
809 	slot--;
810 
811 	rw_enter(&hca->ih_iou_rwlock, RW_READER);
812 	if (ibdma_get_ioc_state(hca, slot) != IBDMA_IOC_PRESENT) {
813 		msg->im_msgbufs_send.im_bufs_mad_hdr->Status =
814 		    IB_DM_DEVMGT_MAD_STAT_NORESP;
815 		rw_exit(&hca->ih_iou_rwlock);
816 		return;
817 	}
818 
819 	if ((low > hi) || (hi - low > 4)) {
820 		msg->im_msgbufs_send.im_bufs_mad_hdr->Status =
821 		    MAD_STATUS_INVALID_FIELD;
822 		rw_exit(&hca->ih_iou_rwlock);
823 		return;
824 	}
825 
826 	if (hi > hca->ih_ioc[slot].ii_profile.ioc_service_entries) {
827 		msg->im_msgbufs_send.im_bufs_mad_hdr->Status =
828 		    MAD_STATUS_INVALID_FIELD;
829 		rw_exit(&hca->ih_iou_rwlock);
830 		return;
831 	}
832 
833 	to_svcp = (ib_dm_srv_t *)msg->im_msgbufs_send.im_bufs_cl_data;
834 	from_svcp = hca->ih_ioc[slot].ii_srvcs + low;
835 	bcopy(from_svcp, to_svcp, sizeof (ib_dm_srv_t) * (hi - low + 1));
836 	rw_exit(&hca->ih_iou_rwlock);
837 }
838 
839 
840 /*
841  * Client API internal helpers
842  */
843 
844 /*
845  * ibdma_hdl_to_ioc()
846  */
847 ibdma_hdl_impl_t *
848 ibdma_get_hdl_impl(ibdma_hdl_t hdl)
849 {
850 	ibdma_hca_t		*hca;
851 	ibdma_hdl_impl_t	*hdl_tmp = hdl;
852 	ibdma_hdl_impl_t	*hdl_impl;
853 
854 	ASSERT(mutex_owned(&ibdma->ms_hca_list_lock));
855 
856 	if (hdl_tmp == NULL) {
857 		cmn_err(CE_NOTE, "get_hdl_impl, NULL handle");
858 		return (NULL);
859 	}
860 
861 	hca = ibdma_find_hca(hdl_tmp->ih_iou_guid);
862 	if (hca == NULL) {
863 		cmn_err(CE_NOTE, "get_hdl_impl, invalid handle, bad IOU");
864 		return (NULL);
865 	}
866 
867 	hdl_impl = list_head(&hca->ih_hdl_list);
868 	while (hdl_impl != NULL) {
869 		if (hdl_impl == hdl_tmp) {
870 			break;
871 		}
872 		hdl_impl = list_next(&hca->ih_hdl_list, hdl_impl);
873 	}
874 	return (hdl_impl);
875 }
876 
877 /*
878  * ibdma_set_ioc_state()
879  *
880  * slot should be 0 based (not DM 1 based slot).
881  *
882  * I/O Unit write lock should be held outside of this function.
883  */
884 static void
885 ibdma_set_ioc_state(ibdma_hca_t *hca, int slot, ibdma_ioc_state_t state)
886 {
887 	uint8_t		cur;
888 	uint16_t	id;
889 
890 	cur = hca->ih_iou.iou_ctrl_list[slot >> 1];
891 	if (slot & 1) {
892 		cur = (cur & 0xF0) | state;
893 	} else {
894 		cur = (cur & 0x0F) | (state << 4);
895 	}
896 	hca->ih_iou.iou_ctrl_list[slot >> 1] = cur;
897 	id = b2h16(hca->ih_iou.iou_changeid);
898 	id++;
899 	hca->ih_iou.iou_changeid = h2b16(id);
900 #ifdef	DEBUG_IBDMA
901 	cmn_err(CE_NOTE, "set_ioc_state, slot offset(%d), value(%d)",
902 	    slot, hca->ih_iou.iou_ctrl_list[slot >> 1]);
903 #endif
904 }
905 
906 /*
907  * ibdma_get_ioc_state()
908  *
909  * slot should be 0 based (not DM 1 based slot).
910  *
911  * I/O Unit read lock should be held outside of this function.
912  */
913 static ibdma_ioc_state_t
914 ibdma_get_ioc_state(ibdma_hca_t *hca, int slot)
915 {
916 	uint8_t		cur;
917 
918 	if (slot >= IBDMA_MAX_IOC)
919 		return (0xFF);
920 
921 	cur = hca->ih_iou.iou_ctrl_list[slot >> 1];
922 	cur = slot & 1 ?  cur & 0x0F : cur >> 4;
923 	return (cur);
924 }
925 
926 /* CLIENT API Implementation */
927 /*
928  * ibdma_ioc_register()
929  *
930  */
931 ibdma_hdl_t
932 ibdma_ioc_register(ib_guid_t iou_guid, ib_dm_ioc_ctrl_profile_t *profile,
933 	ib_dm_srv_t *services)
934 {
935 	int			free_slot = -1;
936 	int			svc_entries;
937 	int			slot;
938 	ibdma_hca_t		*hca;
939 	ibdma_hdl_impl_t	*hdl;
940 
941 	if (profile == NULL || services == NULL) {
942 		cmn_err(CE_NOTE, "ioc_register, bad parameter");
943 		return (NULL);
944 	}
945 
946 	svc_entries = profile->ioc_service_entries;
947 	if (svc_entries == 0) {
948 		cmn_err(CE_NOTE, "ioc_register, bad profile no service");
949 		return (NULL);
950 	}
951 
952 	/*
953 	 * Find the associated I/O Unit.
954 	 */
955 	mutex_enter(&ibdma->ms_hca_list_lock);
956 	hca = ibdma_find_hca(iou_guid);
957 	if (hca == NULL) {
958 		mutex_exit(&ibdma->ms_hca_list_lock);
959 		cmn_err(CE_NOTE, "ioc_register, bad I/O Unit GUID (0x%llx)",
960 		    (u_longlong_t)iou_guid);
961 		return (NULL);
962 	}
963 
964 	rw_enter(&hca->ih_iou_rwlock, RW_WRITER);
965 	for (slot = 0; slot < IBDMA_MAX_IOC; slot++) {
966 		if (hca->ih_ioc[slot].ii_inuse == 0) {
967 			if (free_slot == -1) {
968 				free_slot = slot;
969 			}
970 			continue;
971 		}
972 
973 		if (profile->ioc_guid ==
974 		    hca->ih_ioc[slot].ii_profile.ioc_guid) {
975 			rw_exit(&hca->ih_iou_rwlock);
976 			mutex_exit(&ibdma->ms_hca_list_lock);
977 #ifdef	DEBUG_IBDMA
978 			cmn_err(CE_NOTE, "ioc_register, IOC previously"
979 			    " registered");
980 #endif
981 			return (NULL);
982 		}
983 	}
984 
985 	if (free_slot < 0) {
986 		rw_exit(&hca->ih_iou_rwlock);
987 		cmn_err(CE_NOTE, "ioc_register, error - I/O Unit full");
988 		return (NULL);
989 	}
990 #ifdef	DEBUG_IBDMA
991 	cmn_err(CE_NOTE, "ibdma_ioc_register, assigned to 0 based slot (%d)",
992 	    free_slot);
993 #endif
994 
995 	hca->ih_ioc[free_slot].ii_inuse = 1;
996 	hca->ih_ioc[free_slot].ii_slot  = free_slot;
997 	hca->ih_ioc[free_slot].ii_hcap  = hca;
998 
999 	/*
1000 	 * Allocate local copy of profile and services.
1001 	 */
1002 	hca->ih_ioc[free_slot].ii_srvcs =
1003 	    kmem_zalloc(sizeof (ib_dm_srv_t) * svc_entries, KM_SLEEP);
1004 	bcopy(profile, &hca->ih_ioc[free_slot].ii_profile,
1005 	    sizeof (ib_dm_ioc_ctrl_profile_t));
1006 	bcopy(services, hca->ih_ioc[free_slot].ii_srvcs,
1007 	    sizeof (ib_dm_srv_t) * svc_entries);
1008 
1009 	/*
1010 	 * Update the profile copy with the I/O controller slot assigned.
1011 	 * The slot occupies the lower 8 biths of the vendor ID/slot 32bit
1012 	 * field.
1013 	 */
1014 	profile->ioc_vendorid |= h2b32(free_slot);
1015 
1016 	ibdma_set_ioc_state(hca, free_slot, IBDMA_IOC_PRESENT);
1017 
1018 	hdl = kmem_alloc(sizeof (*hdl), KM_SLEEP);
1019 	hdl->ih_iou_guid = hca->ih_iou_guid;
1020 	hdl->ih_ioc_ndx = (uint8_t)free_slot;
1021 	list_insert_tail(&hca->ih_hdl_list, hdl);
1022 
1023 	rw_exit(&hca->ih_iou_rwlock);
1024 	mutex_exit(&ibdma->ms_hca_list_lock);
1025 
1026 	return ((ibdma_hdl_t)hdl);
1027 }
1028 
1029 /*
1030  * ibdma_ioc_unregister()
1031  *
1032  */
1033 ibdma_status_t
1034 ibdma_ioc_unregister(ibdma_hdl_t hdl)
1035 {
1036 	ibdma_ioc_t		*ioc;
1037 	ibdma_hca_t		*hca;
1038 	int			slot;
1039 	ibdma_hdl_impl_t	*hdl_tmp = hdl;
1040 	ibdma_hdl_impl_t	*hdl_impl;
1041 
1042 	if (hdl == NULL) {
1043 		cmn_err(CE_NOTE, "ioc_unregister, NULL handle");
1044 		return (IBDMA_BAD_PARAM);
1045 	}
1046 
1047 	mutex_enter(&ibdma->ms_hca_list_lock);
1048 	hca = ibdma_find_hca(hdl_tmp->ih_iou_guid);
1049 	if (hca == NULL) {
1050 		cmn_err(CE_NOTE, "ioc_unregsiter, invalid handle, IOU"
1051 		    " not found");
1052 		mutex_exit(&ibdma->ms_hca_list_lock);
1053 		return (IBDMA_BAD_PARAM);
1054 	}
1055 
1056 	hdl_impl = list_head(&hca->ih_hdl_list);
1057 	while (hdl_impl != NULL) {
1058 		if (hdl_impl == hdl_tmp) {
1059 			break;
1060 		}
1061 		hdl_impl = list_next(&hca->ih_hdl_list, hdl_impl);
1062 	}
1063 
1064 	if (hdl_impl == NULL) {
1065 		cmn_err(CE_NOTE, "ioc_unregsiter, invalid handle, not found");
1066 		mutex_exit(&ibdma->ms_hca_list_lock);
1067 		return (IBDMA_BAD_PARAM);
1068 	}
1069 
1070 	list_remove(&hca->ih_hdl_list, hdl_impl);
1071 
1072 	if (hdl_impl->ih_ioc_ndx >= IBDMA_MAX_IOC) {
1073 		cmn_err(CE_NOTE, "ioc_unregister, corrupted handle");
1074 		kmem_free(hdl_impl, sizeof (*hdl_impl));
1075 		mutex_exit(&ibdma->ms_hca_list_lock);
1076 		return (IBDMA_BAD_PARAM);
1077 	}
1078 	ioc = &hca->ih_ioc[hdl_impl->ih_ioc_ndx];
1079 	kmem_free(hdl_impl, sizeof (*hdl_impl));
1080 
1081 	if (ioc->ii_slot > IBDMA_MAX_IOC) {
1082 		cmn_err(CE_NOTE, "ioc_unregister, IOC corrupted, bad"
1083 		    " slot in IOC");
1084 		mutex_exit(&ibdma->ms_hca_list_lock);
1085 		return (IBDMA_BAD_PARAM);
1086 	}
1087 
1088 	rw_enter(&ioc->ii_hcap->ih_iou_rwlock, RW_WRITER);
1089 	if (ioc->ii_inuse == 0) {
1090 		rw_exit(&ioc->ii_hcap->ih_iou_rwlock);
1091 		mutex_exit(&ibdma->ms_hca_list_lock);
1092 		cmn_err(CE_NOTE, "ioc_unregister, slot not in use (%d)",
1093 		    ioc->ii_slot+1);
1094 		return (IBDMA_BAD_PARAM);
1095 	}
1096 
1097 	ASSERT(ioc->ii_srvcs != NULL);
1098 
1099 	slot = ioc->ii_slot;
1100 	hca  = ioc->ii_hcap;
1101 	kmem_free(ioc->ii_srvcs, sizeof (ib_dm_srv_t) *
1102 	    ioc->ii_profile.ioc_service_entries);
1103 	bzero(ioc, sizeof (ibdma_ioc_t));
1104 	ibdma_set_ioc_state(hca, slot, IBDMA_IOC_NOT_INSTALLED);
1105 
1106 	rw_exit(&hca->ih_iou_rwlock);
1107 	mutex_exit(&ibdma->ms_hca_list_lock);
1108 
1109 	return (IBDMA_SUCCESS);
1110 }
1111 
1112 /*
1113  * ibdma_ioc_update()
1114  *
1115  */
1116 ibdma_status_t
1117 ibdma_ioc_update(ibdma_hdl_t hdl, ib_dm_ioc_ctrl_profile_t *profile,
1118 	ib_dm_srv_t *services)
1119 {
1120 	ibdma_ioc_t		*ioc;
1121 	ibdma_hca_t		*hca;
1122 	ibdma_hdl_impl_t	*hdl_tmp = hdl;
1123 	ibdma_hdl_impl_t	*hdl_impl;
1124 
1125 	if (hdl == NULL) {
1126 		cmn_err(CE_NOTE, "ioc_update, NULL handle");
1127 		return (IBDMA_BAD_PARAM);
1128 	}
1129 
1130 	if (profile == NULL || services == NULL) {
1131 		cmn_err(CE_NOTE, "ioc_update, NULL parameter");
1132 		return (IBDMA_BAD_PARAM);
1133 	}
1134 
1135 	mutex_enter(&ibdma->ms_hca_list_lock);
1136 	hca = ibdma_find_hca(hdl_tmp->ih_iou_guid);
1137 	if (hca == NULL) {
1138 		cmn_err(CE_NOTE, "ioc_update, invalid handle, IOU not found");
1139 		mutex_exit(&ibdma->ms_hca_list_lock);
1140 		return (IBDMA_BAD_PARAM);
1141 	}
1142 
1143 	hdl_impl = list_head(&hca->ih_hdl_list);
1144 	while (hdl_impl != NULL) {
1145 		if (hdl_impl == hdl_tmp) {
1146 			break;
1147 		}
1148 		hdl_impl = list_next(&hca->ih_hdl_list, hdl_impl);
1149 	}
1150 
1151 	if (hdl_impl == NULL) {
1152 		cmn_err(CE_NOTE, "ioc_update, invalid handle, not found");
1153 		mutex_exit(&ibdma->ms_hca_list_lock);
1154 		return (IBDMA_BAD_PARAM);
1155 	}
1156 
1157 	if (hdl_impl->ih_ioc_ndx >= IBDMA_MAX_IOC) {
1158 		cmn_err(CE_NOTE, "ioc_update, corrupted handle");
1159 		mutex_exit(&ibdma->ms_hca_list_lock);
1160 		return (IBDMA_BAD_PARAM);
1161 	}
1162 	ioc = &hca->ih_ioc[hdl_impl->ih_ioc_ndx];
1163 
1164 	if (ioc->ii_slot >= IBDMA_MAX_IOC || ioc->ii_hcap == NULL) {
1165 		cmn_err(CE_NOTE, "ioc_update, bad handle (%p)",
1166 		    (void *)hdl);
1167 		mutex_exit(&ibdma->ms_hca_list_lock);
1168 		return (IBDMA_BAD_PARAM);
1169 	}
1170 
1171 	rw_enter(&ioc->ii_hcap->ih_iou_rwlock, RW_WRITER);
1172 	if (ioc->ii_inuse == 0) {
1173 		rw_exit(&ioc->ii_hcap->ih_iou_rwlock);
1174 		mutex_exit(&ibdma->ms_hca_list_lock);
1175 		cmn_err(CE_NOTE, "ioc_udate slot not in use (%d)",
1176 		    ioc->ii_slot+1);
1177 		return (IBDMA_BAD_PARAM);
1178 	}
1179 
1180 	ASSERT(ioc->ii_srvcs != NULL);
1181 
1182 	kmem_free(ioc->ii_srvcs, ioc->ii_profile.ioc_service_entries *
1183 	    sizeof (ib_dm_srv_t));
1184 	ioc->ii_srvcs = kmem_zalloc(profile->ioc_service_entries  *
1185 	    sizeof (ib_dm_srv_t), KM_SLEEP);
1186 
1187 	bcopy(profile, &ioc->ii_profile, sizeof (ib_dm_ioc_ctrl_profile_t));
1188 	bcopy(services, ioc->ii_srvcs, sizeof (ib_dm_srv_t) *
1189 	    profile->ioc_service_entries);
1190 	/*
1191 	 * Update the profile copy with the I/O controller slot assigned.
1192 	 * The slot occupies the lower 8 biths of the vendor ID/slot 32bit
1193 	 * field.
1194 	 */
1195 	profile->ioc_vendorid |= h2b32(ioc->ii_slot);
1196 	ibdma_set_ioc_state(ioc->ii_hcap, ioc->ii_slot, IBDMA_IOC_PRESENT);
1197 	rw_exit(&ioc->ii_hcap->ih_iou_rwlock);
1198 	mutex_exit(&ibdma->ms_hca_list_lock);
1199 
1200 	return (IBDMA_SUCCESS);
1201 }
1202