/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. */ /* * Infiniband Device Management Agent for IB storage. */ #include #include #include #include #include #include #include #include /* IB public interfaces */ #include #include /* * NOTE: The IB Device Management Agent function, like other IB * managers and agents is best implemented as a kernel misc. * module. * Eventually we could modify IBT_DM_AGENT so that we don't need to * open each HCA to receive asynchronous events. */ #define IBDMA_NAME_VERSION "IB Device Management Agent" extern struct mod_ops mod_miscops; static void ibdma_ibt_async_handler(void *clnt, ibt_hca_hdl_t hdl, ibt_async_code_t code, ibt_async_event_t *event); static void ibdma_mad_recv_cb(ibmf_handle_t ibmf_hdl, ibmf_msg_t *msgp, void *args); static void ibdma_create_resp_mad(ibmf_msg_t *msgp); /* * Misc. kernel module for now. */ static struct modlmisc modlmisc = { &mod_miscops, IBDMA_NAME_VERSION }; static struct modlinkage modlinkage = { MODREV_1, (void *)&modlmisc, NULL }; static ibt_clnt_modinfo_t ibdma_ibt_modinfo = { IBTI_V_CURR, IBT_DM_AGENT, ibdma_ibt_async_handler, NULL, "ibdma" }; /* * Module global state allocated at init(). */ static ibdma_mod_state_t *ibdma = NULL; /* * Init/Fini handlers and IBTL HCA management prototypes. */ static int ibdma_init(); static int ibdma_fini(); static int ibdma_ibt_init(); static void ibdma_ibt_fini(); static ibdma_hca_t *ibdma_hca_init(ib_guid_t guid); static void ibdma_hca_fini(ibdma_hca_t *hca); static ibdma_hca_t *ibdma_find_hca(ib_guid_t guid); /* * DevMgmt Agent MAD attribute handlers prototypes. */ static void ibdma_get_class_portinfo(ibmf_msg_t *msg); static void ibdma_get_io_unitinfo(ibdma_hca_t *hca, ibmf_msg_t *msg); static void ibdma_get_ioc_profile(ibdma_hca_t *hca, ibmf_msg_t *msg); static void ibdma_get_ioc_services(ibdma_hca_t *hca, ibmf_msg_t *msg); /* * _init() */ int _init(void) { int status; ASSERT(ibdma == NULL); ibdma = kmem_zalloc(sizeof (*ibdma), KM_SLEEP); ASSERT(ibdma != NULL); status = ibdma_init(); if (status != DDI_SUCCESS) { kmem_free(ibdma, sizeof (*ibdma)); ibdma = NULL; return (status); } status = mod_install(&modlinkage); if (status != DDI_SUCCESS) { cmn_err(CE_NOTE, "_init, mod_install error (%d)", status); (void) ibdma_fini(); kmem_free(ibdma, sizeof (*ibdma)); ibdma = NULL; } return (status); } /* * _info() */ int _info(struct modinfo *modinfop) { return (mod_info(&modlinkage, modinfop)); } /* * _fini() */ int _fini(void) { int status; int slot; ibdma_hca_t *hca; status = mod_remove(&modlinkage); if (status != DDI_SUCCESS) { cmn_err(CE_NOTE, "_fini, mod_remove error (%d)", status); return (status); } /* * Sanity check to see if anyone is not cleaning * up appropriately. */ mutex_enter(&ibdma->ms_hca_list_lock); hca = list_head(&ibdma->ms_hca_list); while (hca != NULL) { for (slot = 0; slot < IBDMA_MAX_IOC; slot++) { if (hca->ih_ioc[slot].ii_inuse) { cmn_err(CE_NOTE, "_fini, IOC %d still attached" " for (0x%0llx)", slot+1, (u_longlong_t)hca->ih_iou_guid); } } hca = list_next(&ibdma->ms_hca_list, hca); } mutex_exit(&ibdma->ms_hca_list_lock); (void) ibdma_fini(); kmem_free(ibdma, sizeof (*ibdma)); return (status); } /* * ibdma_init() * * Initialize I/O Unit structure, generate initial HCA list and register * it port with the IBMF. */ static int ibdma_init() { int status; /* * Global lock and I/O Unit initialization. */ mutex_init(&ibdma->ms_hca_list_lock, NULL, MUTEX_DRIVER, NULL); /* * Discover IB hardware and setup for device management agent * support. */ status = ibdma_ibt_init(); if (status != DDI_SUCCESS) { cmn_err(CE_NOTE, "ibdma_init, ibt_attach failed (%d)", status); mutex_destroy(&ibdma->ms_hca_list_lock); return (status); } return (status); } /* * ibdma_fini() * * Release resource if we are no longer in use. */ static int ibdma_fini() { ibdma_ibt_fini(); mutex_destroy(&ibdma->ms_hca_list_lock); return (DDI_SUCCESS); } /* * ibdma_ibt_async_handler() */ /* ARGSUSED */ static void ibdma_ibt_async_handler(void *clnt, ibt_hca_hdl_t hdl, ibt_async_code_t code, ibt_async_event_t *event) { ibdma_hca_t *hca; switch (code) { case IBT_EVENT_PORT_UP: case IBT_ERROR_PORT_DOWN: case IBT_PORT_CHANGE_EVENT: case IBT_CLNT_REREG_EVENT: break; case IBT_HCA_ATTACH_EVENT: mutex_enter(&ibdma->ms_hca_list_lock); hca = ibdma_hca_init(event->ev_hca_guid); if (hca != NULL) { list_insert_tail(&ibdma->ms_hca_list, hca); cmn_err(CE_NOTE, "hca ibt hdl (%p)", (void *)hca->ih_ibt_hdl); ibdma->ms_num_hcas++; } mutex_exit(&ibdma->ms_hca_list_lock); break; case IBT_HCA_DETACH_EVENT: mutex_enter(&ibdma->ms_hca_list_lock); hca = ibdma_find_hca(event->ev_hca_guid); if (hca != NULL) { list_remove(&ibdma->ms_hca_list, hca); cmn_err(CE_NOTE, "removing hca (%p) (0x%llx)", (void *)hca, hca ? (u_longlong_t)hca->ih_iou_guid : 0x0ll); ibdma_hca_fini(hca); } mutex_exit(&ibdma->ms_hca_list_lock); break; default: #ifdef DEBUG cmn_err(CE_NOTE, "ibt_async_handler, unhandled event(%d)", code); #endif break; } } /* * ibdma_ibt_init() */ static int ibdma_ibt_init() { int status; int hca_cnt; int hca_ndx; ib_guid_t *guid; ibdma_hca_t *hca; /* * Attach to IBTF and get HCA list. */ status = ibt_attach(&ibdma_ibt_modinfo, NULL, ibdma, &ibdma->ms_ibt_hdl); if (status != DDI_SUCCESS) { cmn_err(CE_NOTE, "ibt_init, ibt_attach failed (%d)", status); return (status); } list_create(&ibdma->ms_hca_list, sizeof (ibdma_hca_t), offsetof(ibdma_hca_t, ih_node)); hca_cnt = ibt_get_hca_list(&guid); if (hca_cnt < 1) { #ifdef DEBUG_IBDMA cmn_err(CE_NOTE, "ibt_init, no HCA(s) found"); #endif /* not an error if no HCAs, but nothing more to do here */ return (DDI_SUCCESS); } mutex_enter(&ibdma->ms_hca_list_lock); for (hca_ndx = 0; hca_ndx < hca_cnt; hca_ndx++) { #ifdef DEBUG_IBDMA cmn_err(CE_NOTE, "adding hca GUID(0x%llx)", (u_longlong_t)guid[hca_ndx]); #endif hca = ibdma_hca_init(guid[hca_ndx]); if (hca == NULL) { cmn_err(CE_NOTE, "ibt_init, hca_init GUID(0x%llx)" " failed", (u_longlong_t)guid[hca_ndx]); continue; } list_insert_tail(&ibdma->ms_hca_list, hca); ibdma->ms_num_hcas++; } mutex_exit(&ibdma->ms_hca_list_lock); ibt_free_hca_list(guid, hca_cnt); #ifdef DEBUG_IBDMA cmn_err(CE_NOTE, "Added %d HCA(s)", ibdma->ms_num_hcas); #endif return (DDI_SUCCESS); } /* * ibdma_ibt_fini() */ static void ibdma_ibt_fini() { ibdma_hca_t *hca; ibdma_hca_t *next; mutex_enter(&ibdma->ms_hca_list_lock); hca = list_head(&ibdma->ms_hca_list); while (hca != NULL) { next = list_next(&ibdma->ms_hca_list, hca); list_remove(&ibdma->ms_hca_list, hca); #ifdef DEBUG_IBDMA cmn_err(CE_NOTE, "removing hca (%p) (0x%llx)", (void *)hca, hca ? (u_longlong_t)hca->ih_iou_guid : 0x0ll); cmn_err(CE_NOTE, "hca ibt hdl (%p)", (void *)hca->ih_ibt_hdl); #endif ibdma_hca_fini(hca); hca = next; } list_destroy(&ibdma->ms_hca_list); (void) ibt_detach(ibdma->ms_ibt_hdl); ibdma->ms_ibt_hdl = NULL; ibdma->ms_num_hcas = 0; mutex_exit(&ibdma->ms_hca_list_lock); } /* * ibdma_find_hca() */ static ibdma_hca_t * ibdma_find_hca(ib_guid_t guid) { ibdma_hca_t *hca; ASSERT(mutex_owned(&ibdma->ms_hca_list_lock)); hca = list_head(&ibdma->ms_hca_list); while (hca != NULL) { if (hca->ih_iou_guid == guid) { break; } hca = list_next(&ibdma->ms_hca_list, hca); } return (hca); } /* * ibdma_hca_init() */ static ibdma_hca_t * ibdma_hca_init(ib_guid_t guid) { ibt_status_t status; ibdma_hca_t *hca; ibdma_port_t *port; ibt_hca_attr_t hca_attr; int ndx; ASSERT(mutex_owned(&ibdma->ms_hca_list_lock)); status = ibt_query_hca_byguid(guid, &hca_attr); if (status != IBT_SUCCESS) { cmn_err(CE_NOTE, "hca_init HCA query error (%d)", status); return (NULL); } if (ibdma_find_hca(guid) != NULL) { #ifdef DEBUG_IBDMA cmn_err(CE_NOTE, "hca_init HCA already exists"); #endif return (NULL); } hca = kmem_zalloc(sizeof (ibdma_hca_t) + (hca_attr.hca_nports-1)*sizeof (ibdma_port_t), KM_SLEEP); ASSERT(hca != NULL); hca->ih_nports = hca_attr.hca_nports; rw_init(&hca->ih_iou_rwlock, NULL, RW_DRIVER, NULL); rw_enter(&hca->ih_iou_rwlock, RW_WRITER); hca->ih_iou_guid = guid; hca->ih_iou.iou_changeid = h2b16(1); hca->ih_iou.iou_num_ctrl_slots = IBDMA_MAX_IOC; hca->ih_iou.iou_flag = IB_DM_IOU_OPTIONROM_ABSENT; list_create(&hca->ih_hdl_list, sizeof (ibdma_hdl_impl_t), offsetof(ibdma_hdl_impl_t, ih_node)); rw_exit(&hca->ih_iou_rwlock); /* * It would be better to not open, but IBTL is setup to only allow * certain managers to get async call backs if not open. */ status = ibt_open_hca(ibdma->ms_ibt_hdl, guid, &hca->ih_ibt_hdl); if (status != IBT_SUCCESS) { cmn_err(CE_NOTE, "hca_init() IBT open failed (%d)", status); list_destroy(&hca->ih_hdl_list); rw_destroy(&hca->ih_iou_rwlock); kmem_free(hca, sizeof (ibdma_hca_t) + (hca_attr.hca_nports-1)*sizeof (ibdma_port_t)); return (NULL); } /* * Register with the IB Management Framework and setup MAD call-back. */ for (ndx = 0; ndx < hca->ih_nports; ndx++) { port = &hca->ih_port[ndx]; port->ip_hcap = hca; port->ip_ibmf_reg.ir_ci_guid = hca->ih_iou_guid; port->ip_ibmf_reg.ir_port_num = ndx + 1; port->ip_ibmf_reg.ir_client_class = DEV_MGT_AGENT; status = ibmf_register(&port->ip_ibmf_reg, IBMF_VERSION, 0, NULL, NULL, &port->ip_ibmf_hdl, &port->ip_ibmf_caps); if (status != IBMF_SUCCESS) { cmn_err(CE_NOTE, "hca_init, IBMF register failed (%d)", status); port->ip_ibmf_hdl = NULL; ibdma_hca_fini(hca); return (NULL); } status = ibmf_setup_async_cb(port->ip_ibmf_hdl, IBMF_QP_HANDLE_DEFAULT, ibdma_mad_recv_cb, port, 0); if (status != IBMF_SUCCESS) { cmn_err(CE_NOTE, "hca_init, IBMF cb setup failed (%d)", status); ibdma_hca_fini(hca); return (NULL); } status = ibt_modify_port_byguid(hca->ih_iou_guid, ndx+1, IBT_PORT_SET_DEVMGT, 0); if (status != IBT_SUCCESS) { cmn_err(CE_NOTE, "hca_init, IBT modify port caps" " error (%d)", status); ibdma_hca_fini(hca); return (NULL); } } return (hca); } /* * ibdma_hca_fini() */ static void ibdma_hca_fini(ibdma_hca_t *hca) { int status; int ndx; ibdma_port_t *port; ibdma_hdl_impl_t *hdl; ibdma_hdl_impl_t *hdl_next; ASSERT(mutex_owned(&ibdma->ms_hca_list_lock)); ASSERT(hca != NULL); rw_enter(&hca->ih_iou_rwlock, RW_WRITER); /* * All handles should have been de-registered, but release * any that are outstanding. */ hdl = list_head(&hca->ih_hdl_list); while (hdl != NULL) { hdl_next = list_next(&hca->ih_hdl_list, hdl); list_remove(&hca->ih_hdl_list, hdl); cmn_err(CE_NOTE, "hca_fini, unexpected ibdma user handle" " exists"); kmem_free(hdl, sizeof (*hdl)); hdl = hdl_next; } list_destroy(&hca->ih_hdl_list); /* * Un-register with the IBMF. */ for (ndx = 0; ndx < hca->ih_nports; ndx++) { port = &hca->ih_port[ndx]; port->ip_hcap = NULL; status = ibt_modify_port_byguid(hca->ih_iou_guid, ndx+1, IBT_PORT_RESET_DEVMGT, 0); if (status != IBT_SUCCESS) cmn_err(CE_NOTE, "hca_fini, IBT modify port caps" " error (%d)", status); if (port->ip_ibmf_hdl == NULL) continue; status = ibmf_tear_down_async_cb(port->ip_ibmf_hdl, IBMF_QP_HANDLE_DEFAULT, 0); if (status != IBMF_SUCCESS) cmn_err(CE_NOTE, "hca_fini, IBMF tear down cb" " error (%d)", status); status = ibmf_unregister(&port->ip_ibmf_hdl, 0); if (status != IBMF_SUCCESS) cmn_err(CE_NOTE, "hca_fini, IBMF un-register" " error (%d)", status); port->ip_ibmf_hdl = NULL; } status = ibt_close_hca(hca->ih_ibt_hdl); if (status != IBT_SUCCESS) cmn_err(CE_NOTE, "hca_fini close error (%d)", status); rw_exit(&hca->ih_iou_rwlock); rw_destroy(&hca->ih_iou_rwlock); kmem_free(hca, sizeof (ibdma_hca_t) + (hca->ih_nports-1) * sizeof (ibdma_port_t)); } /* DM IBMF MAD handlers */ /* * ibdma_create_resp_mad() */ static void ibdma_create_resp_mad(ibmf_msg_t *msgp) { /* * Allocate send buffer fix up hdr for response. */ msgp->im_msgbufs_send.im_bufs_mad_hdr = kmem_zalloc(IBDMA_MAD_SIZE, KM_SLEEP); msgp->im_msgbufs_send.im_bufs_cl_hdr = (uchar_t *) msgp->im_msgbufs_send.im_bufs_mad_hdr + sizeof (ib_mad_hdr_t); msgp->im_msgbufs_send.im_bufs_cl_hdr_len = IBDMA_DM_MAD_HDR_SIZE; msgp->im_msgbufs_send.im_bufs_cl_data = ((char *)msgp->im_msgbufs_send.im_bufs_cl_hdr + IBDMA_DM_MAD_HDR_SIZE); msgp->im_msgbufs_send.im_bufs_cl_data_len = IBDMA_MAD_SIZE - sizeof (ib_mad_hdr_t) - IBDMA_DM_MAD_HDR_SIZE; (void) memcpy(msgp->im_msgbufs_send.im_bufs_mad_hdr, msgp->im_msgbufs_recv.im_bufs_mad_hdr, IBDMA_MAD_SIZE); /* * We may want to support a GRH since this is a GMP; not * required for current SRP device manager platforms. */ #if 0 if (msgp->im_msg_flags & IBMF_MSG_FLAGS_GLOBAL_ADDRESS) { ib_gid_t temp = msgp->im_global_addr.ig_recver_gid; msgp->im_global_addr.ig_recver_gid = msgp->im_global_addr.ig_sender_gid; msgp->im_global_addr.ig_sender_gid = temp; } #endif } /* * ibdma_mad_send_cb() */ /* ARGSUSED */ static void ibdma_mad_send_cb(ibmf_handle_t ibmf_hdl, ibmf_msg_t *msgp, void *arg) { /* * Just free the buffers and release the message. */ if (msgp->im_msgbufs_send.im_bufs_mad_hdr != NULL) { kmem_free(msgp->im_msgbufs_send.im_bufs_mad_hdr, IBDMA_MAD_SIZE); msgp->im_msgbufs_send.im_bufs_mad_hdr = NULL; } if (ibmf_free_msg(ibmf_hdl, &msgp) != IBMF_SUCCESS) { cmn_err(CE_NOTE, "mad_send_cb, IBMF message free error"); } } /* * ibdma_mad_recv_cb() */ static void ibdma_mad_recv_cb(ibmf_handle_t ibmf_hdl, ibmf_msg_t *msgp, void *args) { int status; ib_mad_hdr_t *in_mad; ib_mad_hdr_t *out_mad; ibdma_port_t *port = args; ASSERT(msgp != NULL); ASSERT(port != NULL); if (msgp->im_msg_status != IBMF_SUCCESS) { cmn_err(CE_NOTE, "mad_recv_cb, bad MAD receive status (%d)", msgp->im_msg_status); goto drop; } in_mad = msgp->im_msgbufs_recv.im_bufs_mad_hdr; if (in_mad->MgmtClass != MAD_MGMT_CLASS_DEV_MGT) { #ifdef DEBUG_IBDMA cmn_err(CE_NOTE, "mad_recv_cb, MAD not of Dev Mgmt Class"); #endif goto drop; } ibdma_create_resp_mad(msgp); out_mad = msgp->im_msgbufs_send.im_bufs_mad_hdr; out_mad->R_Method = IB_DM_DEVMGT_METHOD_GET_RESP; out_mad->Status = 0; if (in_mad->R_Method == MAD_METHOD_SET) { #ifdef DEBUG_IBDMA cmn_err(CE_NOTE, "mad_recv_cb, no attributes supported" " for set"); #endif out_mad->Status = MAD_STATUS_UNSUPP_METHOD_ATTR; goto send_resp; } if (in_mad->R_Method != MAD_METHOD_GET) { #ifdef DEBUG_IBDMA cmn_err(CE_NOTE, "mad_recv_cb, no attributes supported" " for set"); #endif out_mad->Status = MAD_STATUS_UNSUPP_METHOD; goto send_resp; } /* * Process a GET method. */ switch (b2h16(in_mad->AttributeID)) { case IB_DM_ATTR_CLASSPORTINFO: ibdma_get_class_portinfo(msgp); break; case IB_DM_ATTR_IO_UNITINFO: ibdma_get_io_unitinfo(port->ip_hcap, msgp); break; case IB_DM_ATTR_IOC_CTRL_PROFILE: ibdma_get_ioc_profile(port->ip_hcap, msgp); break; case IB_DM_ATTR_SERVICE_ENTRIES: ibdma_get_ioc_services(port->ip_hcap, msgp); break; default: out_mad->Status = MAD_STATUS_UNSUPP_METHOD_ATTR; break; } send_resp: status = ibmf_msg_transport(ibmf_hdl, IBMF_QP_HANDLE_DEFAULT, msgp, NULL, ibdma_mad_send_cb, NULL, 0); if (status != IBMF_SUCCESS) { cmn_err(CE_NOTE, "mad_recv_cb, send error (%d)", status); ibdma_mad_send_cb(ibmf_hdl, msgp, NULL); } return; drop: status = ibmf_free_msg(ibmf_hdl, &msgp); if (status != IBMF_SUCCESS) { cmn_err(CE_NOTE, "mad_recv_cb, error dropping (%d)", status); } } /* * ibdma_get_class_portinfo() */ static void ibdma_get_class_portinfo(ibmf_msg_t *msg) { ib_mad_classportinfo_t *cpip; cpip = (ib_mad_classportinfo_t *)msg->im_msgbufs_send.im_bufs_cl_data; bzero(cpip, sizeof (*cpip)); cpip->BaseVersion = MAD_CLASS_BASE_VERS_1; cpip->ClassVersion = IB_DM_CLASS_VERSION_1; cpip->RespTimeValue = h2b32(IBDMA_DM_RESP_TIME); } /* * ibdma_get_io_unitinfo() */ static void ibdma_get_io_unitinfo(ibdma_hca_t *hca, ibmf_msg_t *msg) { ib_dm_io_unitinfo_t *uip; uip = (ib_dm_io_unitinfo_t *)msg->im_msgbufs_send.im_bufs_cl_data; rw_enter(&hca->ih_iou_rwlock, RW_READER); bcopy(&hca->ih_iou, uip, sizeof (ib_dm_io_unitinfo_t)); rw_exit(&hca->ih_iou_rwlock); } /* * ibdma_get_ioc_profile() */ static void ibdma_get_ioc_profile(ibdma_hca_t *hca, ibmf_msg_t *msg) { ib_dm_ioc_ctrl_profile_t *iocp; uint32_t slot; ASSERT(msg != NULL); slot = b2h32(msg->im_msgbufs_recv.im_bufs_mad_hdr->AttributeModifier); iocp = (ib_dm_ioc_ctrl_profile_t *) msg->im_msgbufs_send.im_bufs_cl_data; if (slot == 0 || slot > IBDMA_MAX_IOC) { msg->im_msgbufs_send.im_bufs_mad_hdr->Status = MAD_STATUS_INVALID_FIELD; return; } slot--; rw_enter(&hca->ih_iou_rwlock, RW_READER); if (ibdma_get_ioc_state(hca, slot) == IBDMA_IOC_PRESENT) { bcopy(&hca->ih_ioc[slot].ii_profile, iocp, sizeof (ib_dm_ioc_ctrl_profile_t)); } else { msg->im_msgbufs_send.im_bufs_mad_hdr->Status = IB_DM_DEVMGT_MAD_STAT_NORESP; } rw_exit(&hca->ih_iou_rwlock); } /* * ibdma_get_ioc_services() */ static void ibdma_get_ioc_services(ibdma_hca_t *hca, ibmf_msg_t *msg) { ib_dm_srv_t *to_svcp; ib_dm_srv_t *from_svcp; uint32_t slot; uint8_t hi; uint8_t low; ASSERT(msg != NULL); slot = b2h32(msg->im_msgbufs_recv.im_bufs_mad_hdr->AttributeModifier); hi = (slot >> 8) & 0x00FF; low = slot & 0x00FF; slot = (slot >> 16) & 0x0FFFF; if (slot == 0 || slot > IBDMA_MAX_IOC) { msg->im_msgbufs_send.im_bufs_mad_hdr->Status = MAD_STATUS_INVALID_FIELD; return; } slot--; rw_enter(&hca->ih_iou_rwlock, RW_READER); if (ibdma_get_ioc_state(hca, slot) != IBDMA_IOC_PRESENT) { msg->im_msgbufs_send.im_bufs_mad_hdr->Status = IB_DM_DEVMGT_MAD_STAT_NORESP; rw_exit(&hca->ih_iou_rwlock); return; } if ((low > hi) || (hi - low > 4)) { msg->im_msgbufs_send.im_bufs_mad_hdr->Status = MAD_STATUS_INVALID_FIELD; rw_exit(&hca->ih_iou_rwlock); return; } if (hi > hca->ih_ioc[slot].ii_profile.ioc_service_entries) { msg->im_msgbufs_send.im_bufs_mad_hdr->Status = MAD_STATUS_INVALID_FIELD; rw_exit(&hca->ih_iou_rwlock); return; } to_svcp = (ib_dm_srv_t *)msg->im_msgbufs_send.im_bufs_cl_data; from_svcp = hca->ih_ioc[slot].ii_srvcs + low; bcopy(from_svcp, to_svcp, sizeof (ib_dm_srv_t) * (hi - low + 1)); rw_exit(&hca->ih_iou_rwlock); } /* * Client API internal helpers */ /* * ibdma_hdl_to_ioc() */ ibdma_hdl_impl_t * ibdma_get_hdl_impl(ibdma_hdl_t hdl) { ibdma_hca_t *hca; ibdma_hdl_impl_t *hdl_tmp = hdl; ibdma_hdl_impl_t *hdl_impl; ASSERT(mutex_owned(&ibdma->ms_hca_list_lock)); if (hdl_tmp == NULL) { cmn_err(CE_NOTE, "get_hdl_impl, NULL handle"); return (NULL); } hca = ibdma_find_hca(hdl_tmp->ih_iou_guid); if (hca == NULL) { cmn_err(CE_NOTE, "get_hdl_impl, invalid handle, bad IOU"); return (NULL); } hdl_impl = list_head(&hca->ih_hdl_list); while (hdl_impl != NULL) { if (hdl_impl == hdl_tmp) { break; } hdl_impl = list_next(&hca->ih_hdl_list, hdl_impl); } return (hdl_impl); } /* * ibdma_set_ioc_state() * * slot should be 0 based (not DM 1 based slot). * * I/O Unit write lock should be held outside of this function. */ static void ibdma_set_ioc_state(ibdma_hca_t *hca, int slot, ibdma_ioc_state_t state) { uint8_t cur; uint16_t id; cur = hca->ih_iou.iou_ctrl_list[slot >> 1]; if (slot & 1) { cur = (cur & 0xF0) | state; } else { cur = (cur & 0x0F) | (state << 4); } hca->ih_iou.iou_ctrl_list[slot >> 1] = cur; id = b2h16(hca->ih_iou.iou_changeid); id++; hca->ih_iou.iou_changeid = h2b16(id); #ifdef DEBUG_IBDMA cmn_err(CE_NOTE, "set_ioc_state, slot offset(%d), value(%d)", slot, hca->ih_iou.iou_ctrl_list[slot >> 1]); #endif } /* * ibdma_get_ioc_state() * * slot should be 0 based (not DM 1 based slot). * * I/O Unit read lock should be held outside of this function. */ static ibdma_ioc_state_t ibdma_get_ioc_state(ibdma_hca_t *hca, int slot) { uint8_t cur; if (slot >= IBDMA_MAX_IOC) return (0xFF); cur = hca->ih_iou.iou_ctrl_list[slot >> 1]; cur = slot & 1 ? cur & 0x0F : cur >> 4; return (cur); } /* CLIENT API Implementation */ /* * ibdma_ioc_register() * */ ibdma_hdl_t ibdma_ioc_register(ib_guid_t iou_guid, ib_dm_ioc_ctrl_profile_t *profile, ib_dm_srv_t *services) { int free_slot = -1; int svc_entries; int slot; ibdma_hca_t *hca; ibdma_hdl_impl_t *hdl; if (profile == NULL || services == NULL) { cmn_err(CE_NOTE, "ioc_register, bad parameter"); return (NULL); } svc_entries = profile->ioc_service_entries; if (svc_entries == 0) { cmn_err(CE_NOTE, "ioc_register, bad profile no service"); return (NULL); } /* * Find the associated I/O Unit. */ mutex_enter(&ibdma->ms_hca_list_lock); hca = ibdma_find_hca(iou_guid); if (hca == NULL) { mutex_exit(&ibdma->ms_hca_list_lock); cmn_err(CE_NOTE, "ioc_register, bad I/O Unit GUID (0x%llx)", (u_longlong_t)iou_guid); return (NULL); } rw_enter(&hca->ih_iou_rwlock, RW_WRITER); for (slot = 0; slot < IBDMA_MAX_IOC; slot++) { if (hca->ih_ioc[slot].ii_inuse == 0) { if (free_slot == -1) { free_slot = slot; } continue; } if (profile->ioc_guid == hca->ih_ioc[slot].ii_profile.ioc_guid) { rw_exit(&hca->ih_iou_rwlock); mutex_exit(&ibdma->ms_hca_list_lock); #ifdef DEBUG_IBDMA cmn_err(CE_NOTE, "ioc_register, IOC previously" " registered"); #endif return (NULL); } } if (free_slot < 0) { rw_exit(&hca->ih_iou_rwlock); cmn_err(CE_NOTE, "ioc_register, error - I/O Unit full"); return (NULL); } #ifdef DEBUG_IBDMA cmn_err(CE_NOTE, "ibdma_ioc_register, assigned to 0 based slot (%d)", free_slot); #endif hca->ih_ioc[free_slot].ii_inuse = 1; hca->ih_ioc[free_slot].ii_slot = free_slot; hca->ih_ioc[free_slot].ii_hcap = hca; /* * Allocate local copy of profile and services. */ hca->ih_ioc[free_slot].ii_srvcs = kmem_zalloc(sizeof (ib_dm_srv_t) * svc_entries, KM_SLEEP); bcopy(profile, &hca->ih_ioc[free_slot].ii_profile, sizeof (ib_dm_ioc_ctrl_profile_t)); bcopy(services, hca->ih_ioc[free_slot].ii_srvcs, sizeof (ib_dm_srv_t) * svc_entries); /* * Update the profile copy with the I/O controller slot assigned. * The slot occupies the lower 8 biths of the vendor ID/slot 32bit * field. */ profile->ioc_vendorid |= h2b32(free_slot); ibdma_set_ioc_state(hca, free_slot, IBDMA_IOC_PRESENT); hdl = kmem_alloc(sizeof (*hdl), KM_SLEEP); hdl->ih_iou_guid = hca->ih_iou_guid; hdl->ih_ioc_ndx = (uint8_t)free_slot; list_insert_tail(&hca->ih_hdl_list, hdl); rw_exit(&hca->ih_iou_rwlock); mutex_exit(&ibdma->ms_hca_list_lock); return ((ibdma_hdl_t)hdl); } /* * ibdma_ioc_unregister() * */ ibdma_status_t ibdma_ioc_unregister(ibdma_hdl_t hdl) { ibdma_ioc_t *ioc; ibdma_hca_t *hca; int slot; ibdma_hdl_impl_t *hdl_tmp = hdl; ibdma_hdl_impl_t *hdl_impl; if (hdl == NULL) { cmn_err(CE_NOTE, "ioc_unregister, NULL handle"); return (IBDMA_BAD_PARAM); } mutex_enter(&ibdma->ms_hca_list_lock); hca = ibdma_find_hca(hdl_tmp->ih_iou_guid); if (hca == NULL) { cmn_err(CE_NOTE, "ioc_unregsiter, invalid handle, IOU" " not found"); mutex_exit(&ibdma->ms_hca_list_lock); return (IBDMA_BAD_PARAM); } hdl_impl = list_head(&hca->ih_hdl_list); while (hdl_impl != NULL) { if (hdl_impl == hdl_tmp) { break; } hdl_impl = list_next(&hca->ih_hdl_list, hdl_impl); } if (hdl_impl == NULL) { cmn_err(CE_NOTE, "ioc_unregsiter, invalid handle, not found"); mutex_exit(&ibdma->ms_hca_list_lock); return (IBDMA_BAD_PARAM); } list_remove(&hca->ih_hdl_list, hdl_impl); if (hdl_impl->ih_ioc_ndx >= IBDMA_MAX_IOC) { cmn_err(CE_NOTE, "ioc_unregister, corrupted handle"); kmem_free(hdl_impl, sizeof (*hdl_impl)); mutex_exit(&ibdma->ms_hca_list_lock); return (IBDMA_BAD_PARAM); } ioc = &hca->ih_ioc[hdl_impl->ih_ioc_ndx]; kmem_free(hdl_impl, sizeof (*hdl_impl)); if (ioc->ii_slot > IBDMA_MAX_IOC) { cmn_err(CE_NOTE, "ioc_unregister, IOC corrupted, bad" " slot in IOC"); mutex_exit(&ibdma->ms_hca_list_lock); return (IBDMA_BAD_PARAM); } rw_enter(&ioc->ii_hcap->ih_iou_rwlock, RW_WRITER); if (ioc->ii_inuse == 0) { rw_exit(&ioc->ii_hcap->ih_iou_rwlock); mutex_exit(&ibdma->ms_hca_list_lock); cmn_err(CE_NOTE, "ioc_unregister, slot not in use (%d)", ioc->ii_slot+1); return (IBDMA_BAD_PARAM); } ASSERT(ioc->ii_srvcs != NULL); slot = ioc->ii_slot; hca = ioc->ii_hcap; kmem_free(ioc->ii_srvcs, sizeof (ib_dm_srv_t) * ioc->ii_profile.ioc_service_entries); bzero(ioc, sizeof (ibdma_ioc_t)); ibdma_set_ioc_state(hca, slot, IBDMA_IOC_NOT_INSTALLED); rw_exit(&hca->ih_iou_rwlock); mutex_exit(&ibdma->ms_hca_list_lock); return (IBDMA_SUCCESS); } /* * ibdma_ioc_update() * */ ibdma_status_t ibdma_ioc_update(ibdma_hdl_t hdl, ib_dm_ioc_ctrl_profile_t *profile, ib_dm_srv_t *services) { ibdma_ioc_t *ioc; ibdma_hca_t *hca; ibdma_hdl_impl_t *hdl_tmp = hdl; ibdma_hdl_impl_t *hdl_impl; if (hdl == NULL) { cmn_err(CE_NOTE, "ioc_update, NULL handle"); return (IBDMA_BAD_PARAM); } if (profile == NULL || services == NULL) { cmn_err(CE_NOTE, "ioc_update, NULL parameter"); return (IBDMA_BAD_PARAM); } mutex_enter(&ibdma->ms_hca_list_lock); hca = ibdma_find_hca(hdl_tmp->ih_iou_guid); if (hca == NULL) { cmn_err(CE_NOTE, "ioc_update, invalid handle, IOU not found"); mutex_exit(&ibdma->ms_hca_list_lock); return (IBDMA_BAD_PARAM); } hdl_impl = list_head(&hca->ih_hdl_list); while (hdl_impl != NULL) { if (hdl_impl == hdl_tmp) { break; } hdl_impl = list_next(&hca->ih_hdl_list, hdl_impl); } if (hdl_impl == NULL) { cmn_err(CE_NOTE, "ioc_update, invalid handle, not found"); mutex_exit(&ibdma->ms_hca_list_lock); return (IBDMA_BAD_PARAM); } if (hdl_impl->ih_ioc_ndx >= IBDMA_MAX_IOC) { cmn_err(CE_NOTE, "ioc_update, corrupted handle"); mutex_exit(&ibdma->ms_hca_list_lock); return (IBDMA_BAD_PARAM); } ioc = &hca->ih_ioc[hdl_impl->ih_ioc_ndx]; if (ioc->ii_slot >= IBDMA_MAX_IOC || ioc->ii_hcap == NULL) { cmn_err(CE_NOTE, "ioc_update, bad handle (%p)", (void *)hdl); mutex_exit(&ibdma->ms_hca_list_lock); return (IBDMA_BAD_PARAM); } rw_enter(&ioc->ii_hcap->ih_iou_rwlock, RW_WRITER); if (ioc->ii_inuse == 0) { rw_exit(&ioc->ii_hcap->ih_iou_rwlock); mutex_exit(&ibdma->ms_hca_list_lock); cmn_err(CE_NOTE, "ioc_udate slot not in use (%d)", ioc->ii_slot+1); return (IBDMA_BAD_PARAM); } ASSERT(ioc->ii_srvcs != NULL); kmem_free(ioc->ii_srvcs, ioc->ii_profile.ioc_service_entries * sizeof (ib_dm_srv_t)); ioc->ii_srvcs = kmem_zalloc(profile->ioc_service_entries * sizeof (ib_dm_srv_t), KM_SLEEP); bcopy(profile, &ioc->ii_profile, sizeof (ib_dm_ioc_ctrl_profile_t)); bcopy(services, ioc->ii_srvcs, sizeof (ib_dm_srv_t) * profile->ioc_service_entries); /* * Update the profile copy with the I/O controller slot assigned. * The slot occupies the lower 8 biths of the vendor ID/slot 32bit * field. */ profile->ioc_vendorid |= h2b32(ioc->ii_slot); ibdma_set_ioc_state(ioc->ii_hcap, ioc->ii_slot, IBDMA_IOC_PRESENT); rw_exit(&ioc->ii_hcap->ih_iou_rwlock); mutex_exit(&ibdma->ms_hca_list_lock); return (IBDMA_SUCCESS); }