/* * 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) 2010, Oracle and/or its affiliates. All rights reserved. */ /* * sol_umad.c * * ofuv user MAD kernel agent module * * Enables functionality of the OFED 1.3 Linux based MAD application code. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* sys/policy.h should include this, but... */ #define MAX_NAME_LEN 32 #if defined(DEBUG) static char *sol_umad_dbg_str = "sol_umad"; #endif /* Local definitions */ static void *umad_statep; static struct cb_ops umad_cb_ops = { .cb_open = umad_open, .cb_close = umad_close, .cb_strategy = nodev, .cb_print = nodev, .cb_dump = nodev, .cb_read = umad_read, .cb_write = umad_write, .cb_ioctl = umad_ioctl, .cb_devmap = nodev, .cb_mmap = nodev, .cb_segmap = nodev, .cb_chpoll = umad_poll, .cb_prop_op = umad_prop_op, .cb_str = NULL, .cb_flag = D_NEW | D_MP, .cb_rev = CB_REV, .cb_aread = nodev, .cb_awrite = nodev }; static struct dev_ops umad_dev_ops = { .devo_rev = DEVO_REV, .devo_refcnt = 0, .devo_getinfo = umad_getinfo, .devo_identify = nulldev, .devo_probe = nulldev, .devo_attach = umad_attach, .devo_detach = umad_detach, .devo_reset = nodev, .devo_cb_ops = &umad_cb_ops, .devo_bus_ops = NULL, .devo_power = nodev, .devo_quiesce = ddi_quiesce_not_needed }; static struct modldrv umad_modldrv = { .drv_modops = &mod_driverops, .drv_linkinfo = "Solaris IB user MAD kernel driver", .drv_dev_ops = &umad_dev_ops }; static struct modlinkage modlinkage = { .ml_rev = MODREV_1, .ml_linkage = { [0] = &umad_modldrv, [1] = NULL, } }; static ibt_clnt_modinfo_t ibt_clnt_modinfo = { .mi_ibt_version = IBTI_V_CURR, .mi_clnt_class = IBT_USER, .mi_async_handler = umad_async_handler, .mi_reserved = NULL, .mi_clnt_name = "sol_umad" }; #define MAX_MAD_TO_IBMF_MAPPINGS 4 /* Max of 4 MADs to 1 IBMF */ const struct ibmf_class_to_mad_type { enum _ibmf_client_type_t ibmf_class; uint8_t mad_types[MAX_MAD_TO_IBMF_MAPPINGS]; } ibmf_class_to_mad_types[] = { {SUBN_MANAGER, {MAD_MGMT_CLASS_SUBN_LID_ROUTED, MAD_MGMT_CLASS_SUBN_DIRECT_ROUTE, 0}}, {0, {0}} }; const enum _ibmf_client_type_t umad_type_to_ibmf_class[256] = { 0, /* 0x00 Reserved */ SUBN_MANAGER, /* 0x01 CLASS_SUBN_LID_ROUTED */ 0, /* 0x02 Reserved */ SUBN_ADM_AGENT, /* 0x03 CLASS_SUBN_ADM */ PERF_MANAGER, /* 0x04 CLASS_PERF_MGMT */ BM_AGENT, /* 0x05 CLASS_BM */ DEV_MGT_AGENT, /* 0x06 CLASS_DEVICE_MGMT */ COMM_MGT_MANAGER_AGENT, /* 0x07 CLASS_CM */ SNMP_MANAGER_AGENT, /* 0x08 CLASS_SNMP */ VENDOR_09_MANAGER_AGENT, /* 0x09 */ VENDOR_0A_MANAGER_AGENT, /* 0x0A */ VENDOR_0B_MANAGER_AGENT, /* 0x0B */ VENDOR_0C_MANAGER_AGENT, /* 0x0C */ VENDOR_0D_MANAGER_AGENT, /* 0x0D */ VENDOR_0E_MANAGER_AGENT, /* 0x0E */ VENDOR_0F_MANAGER_AGENT, /* 0x0F */ APPLICATION_10_MANAGER_AGENT, /* 0x10 */ APPLICATION_11_MANAGER_AGENT, /* 0x11 */ APPLICATION_12_MANAGER_AGENT, /* 0x12 */ APPLICATION_13_MANAGER_AGENT, /* 0x13 */ APPLICATION_14_MANAGER_AGENT, /* 0x14 */ APPLICATION_15_MANAGER_AGENT, /* 0x15 */ APPLICATION_16_MANAGER_AGENT, /* 0x16 */ APPLICATION_17_MANAGER_AGENT, /* 0x17 */ APPLICATION_18_MANAGER_AGENT, /* 0x18 */ APPLICATION_19_MANAGER_AGENT, /* 0x19 */ APPLICATION_1A_MANAGER_AGENT, /* 0x1A */ APPLICATION_1B_MANAGER_AGENT, /* 0x1B */ APPLICATION_1C_MANAGER_AGENT, /* 0x1C */ APPLICATION_1D_MANAGER_AGENT, /* 0x1D */ APPLICATION_1E_MANAGER_AGENT, /* 0x1E */ APPLICATION_1F_MANAGER_AGENT, /* 0x1F */ APPLICATION_20_MANAGER_AGENT, /* 0x20 */ APPLICATION_21_MANAGER_AGENT, /* 0x21 */ APPLICATION_22_MANAGER_AGENT, /* 0x22 */ APPLICATION_23_MANAGER_AGENT, /* 0x23 */ APPLICATION_24_MANAGER_AGENT, /* 0x24 */ APPLICATION_25_MANAGER_AGENT, /* 0x25 */ APPLICATION_26_MANAGER_AGENT, /* 0x26 */ APPLICATION_27_MANAGER_AGENT, /* 0x27 */ APPLICATION_28_MANAGER_AGENT, /* 0x28 */ APPLICATION_29_MANAGER_AGENT, /* 0x29 */ APPLICATION_2A_MANAGER_AGENT, /* 0x2A */ APPLICATION_2B_MANAGER_AGENT, /* 0x2B */ APPLICATION_2C_MANAGER_AGENT, /* 0x2C */ APPLICATION_2D_MANAGER_AGENT, /* 0x2D */ APPLICATION_2E_MANAGER_AGENT, /* 0x2E */ APPLICATION_2F_MANAGER_AGENT, /* 0x2F */ VENDOR_30_MANAGER_AGENT, /* 0x30 */ VENDOR_31_MANAGER_AGENT, /* 0x31 */ VENDOR_32_MANAGER_AGENT, /* 0x32 */ VENDOR_33_MANAGER_AGENT, /* 0x33 */ VENDOR_34_MANAGER_AGENT, /* 0x34 */ VENDOR_35_MANAGER_AGENT, /* 0x35 */ VENDOR_36_MANAGER_AGENT, /* 0x36 */ VENDOR_37_MANAGER_AGENT, /* 0x37 */ VENDOR_38_MANAGER_AGENT, /* 0x38 */ VENDOR_39_MANAGER_AGENT, /* 0x39 */ VENDOR_3A_MANAGER_AGENT, /* 0x3A */ VENDOR_3B_MANAGER_AGENT, /* 0x3B */ VENDOR_3C_MANAGER_AGENT, /* 0x3C */ VENDOR_3D_MANAGER_AGENT, /* 0x3D */ VENDOR_3E_MANAGER_AGENT, /* 0x3E */ VENDOR_3F_MANAGER_AGENT, /* 0x3F */ VENDOR_40_MANAGER_AGENT, VENDOR_41_MANAGER_AGENT, VENDOR_42_MANAGER_AGENT, VENDOR_43_MANAGER_AGENT, VENDOR_44_MANAGER_AGENT, VENDOR_45_MANAGER_AGENT, VENDOR_46_MANAGER_AGENT, VENDOR_47_MANAGER_AGENT, VENDOR_48_MANAGER_AGENT, VENDOR_49_MANAGER_AGENT, VENDOR_4A_MANAGER_AGENT, VENDOR_4B_MANAGER_AGENT, VENDOR_4C_MANAGER_AGENT, VENDOR_4D_MANAGER_AGENT, VENDOR_4E_MANAGER_AGENT, VENDOR_4F_MANAGER_AGENT, 0, /* 0x50 Reserved */ 0, /* 0x51 Reserved */ 0, /* 0x52 Reserved */ 0, /* 0x53 Reserved */ 0, /* 0x54 Reserved */ 0, /* 0x55 Reserved */ 0, /* 0x56 Reserved */ 0, /* 0x57 Reserved */ 0, /* 0x58 Reserved */ 0, /* 0x59 Reserved */ 0, /* 0x5A Reserved */ 0, /* 0x5B Reserved */ 0, /* 0x5C Reserved */ 0, /* 0x5D Reserved */ 0, /* 0x5E Reserved */ 0, /* 0x5F Reserved */ 0, /* 0x60 Reserved */ 0, /* 0x61 Reserved */ 0, /* 0x62 Reserved */ 0, /* 0x63 Reserved */ 0, /* 0x64 Reserved */ 0, /* 0x65 Reserved */ 0, /* 0x66 Reserved */ 0, /* 0x67 Reserved */ 0, /* 0x68 Reserved */ 0, /* 0x69 Reserved */ 0, /* 0x6A Reserved */ 0, /* 0x6B Reserved */ 0, /* 0x6C Reserved */ 0, /* 0x6D Reserved */ 0, /* 0x6E Reserved */ 0, /* 0x6F Reserved */ 0, /* 0x70 Reserved */ 0, /* 0x71 Reserved */ 0, /* 0x72 Reserved */ 0, /* 0x73 Reserved */ 0, /* 0x74 Reserved */ 0, /* 0x75 Reserved */ 0, /* 0x76 Reserved */ 0, /* 0x77 Reserved */ 0, /* 0x78 Reserved */ 0, /* 0x79 Reserved */ 0, /* 0x7A Reserved */ 0, /* 0x7B Reserved */ 0, /* 0x7C Reserved */ 0, /* 0x7D Reserved */ 0, /* 0x7E Reserved */ 0, /* 0x7F Reserved */ 0, /* 0x80 Reserved */ SUBN_MANAGER, /* 0x81 CLASS_SUBN_DIRECT_ROUTE */ 0, /* 0x82 Reserved */ 0, /* 0x82 Reserved */ 0, /* 0x84 Reserved */ 0, /* 0x85 Reserved */ 0, /* 0x86 Reserved */ 0, /* 0x87 Reserved */ 0, /* 0x88 Reserved */ 0, /* 0x89 Reserved */ 0, /* 0x8A Reserved */ 0, /* 0x8B Reserved */ 0, /* 0x8C Reserved */ 0, /* 0x8D Reserved */ 0, /* 0x8E Reserved */ 0, /* 0x8f Reserved */ 0, /* 0x90 Reserved */ 0, /* 0x91 Reserved */ 0, /* 0x92 Reserved */ 0, /* 0x93 Reserved */ 0, /* 0x94 Reserved */ 0, /* 0x95 Reserved */ 0, /* 0x96 Reserved */ 0, /* 0x97 Reserved */ 0, /* 0x98 Reserved */ 0, /* 0x99 Reserved */ 0, /* 0x9A Reserved */ 0, /* 0x9B Reserved */ 0, /* 0x9C Reserved */ 0, /* 0x9D Reserved */ 0, /* 0x9E Reserved */ 0, /* 0x9F Reserved */ 0, /* 0xA0 Reserved */ 0, /* 0xA1 Reserved */ 0, /* 0xA2 Reserved */ 0, /* 0xA3 Reserved */ 0, /* 0xA4 Reserved */ 0, /* 0xA5 Reserved */ 0, /* 0xA6 Reserved */ 0, /* 0xA7 Reserved */ 0, /* 0xA8 Reserved */ 0, /* 0xA9 Reserved */ 0, /* 0xAA Reserved */ 0, /* 0xAB Reserved */ 0, /* 0xAC Reserved */ 0, /* 0xAD Reserved */ 0, /* 0xAE Reserved */ 0, /* 0xAF Reserved */ 0, /* 0xB0 Reserved */ 0, /* 0xB1 Reserved */ 0, /* 0xB2 Reserved */ 0, /* 0xB3 Reserved */ 0, /* 0xB4 Reserved */ 0, /* 0xB5 Reserved */ 0, /* 0xB6 Reserved */ 0, /* 0xB7 Reserved */ 0, /* 0xB8 Reserved */ 0, /* 0xB9 Reserved */ 0, /* 0xBA Reserved */ 0, /* 0xBB Reserved */ 0, /* 0xBC Reserved */ 0, /* 0xBD Reserved */ 0, /* 0xBE Reserved */ 0, /* 0xBF Reserved */ 0, /* 0xC0 Reserved */ 0, /* 0xC1 Reserved */ 0, /* 0xC2 Reserved */ 0, /* 0xC3 Reserved */ 0, /* 0xC4 Reserved */ 0, /* 0xC5 Reserved */ 0, /* 0xC6 Reserved */ 0, /* 0xC7 Reserved */ 0, /* 0xC8 Reserved */ 0, /* 0xC9 Reserved */ 0, /* 0xCA Reserved */ 0, /* 0xCB Reserved */ 0, /* 0xCC Reserved */ 0, /* 0xCD Reserved */ 0, /* 0xCE Reserved */ 0, /* 0xCF Reserved */ 0, /* 0xD0 Reserved */ 0, /* 0xD1 Reserved */ 0, /* 0xD2 Reserved */ 0, /* 0xD3 Reserved */ 0, /* 0xD4 Reserved */ 0, /* 0xD5 Reserved */ 0, /* 0xD6 Reserved */ 0, /* 0xD7 Reserved */ 0, /* 0xD8 Reserved */ 0, /* 0xD9 Reserved */ 0, /* 0xDA Reserved */ 0, /* 0xDB Reserved */ 0, /* 0xDC Reserved */ 0, /* 0xDD Reserved */ 0, /* 0xDE Reserved */ 0, /* 0xDF Reserved */ 0, /* 0xE0 Reserved */ 0, /* 0xE1 Reserved */ 0, /* 0xE2 Reserved */ 0, /* 0xE3 Reserved */ 0, /* 0xE4 Reserved */ 0, /* 0xE5 Reserved */ 0, /* 0xE6 Reserved */ 0, /* 0xE7 Reserved */ 0, /* 0xE8 Reserved */ 0, /* 0xE9 Reserved */ 0, /* 0xEA Reserved */ 0, /* 0xEB Reserved */ 0, /* 0xEC Reserved */ 0, /* 0xED Reserved */ 0, /* 0xEE Reserved */ 0, /* 0xEF Reserved */ 0, /* 0xF0 Reserved */ 0, /* 0xF1 Reserved */ 0, /* 0xF2 Reserved */ 0, /* 0xF3 Reserved */ 0, /* 0xF4 Reserved */ 0, /* 0xF5 Reserved */ 0, /* 0xF6 Reserved */ 0, /* 0xF7 Reserved */ 0, /* 0xF8 Reserved */ 0, /* 0xF9 Reserved */ 0, /* 0xFA Reserved */ 0, /* 0xFB Reserved */ 0, /* 0xFC Reserved */ 0, /* 0xFD Reserved */ 0, /* 0xFE Reserved */ 0, /* 0xFF Reserved */ }; /* * Function: * umad_init_port_info * Input: * info - driver info * hca - hca info * Output: * port - port info * Returns: * None * Called by: * umad_init_hca_info * Description: * - Associates an hca to a port. * - Initializes user context list for the port passed in * - Initializes mutex to protect the user context list */ static void umad_init_port_info(const umad_hca_info_t *hca, umad_port_info_t *port) { port->port_hca = hca; llist_head_init(&port->port_ibmf_regs, NULL); mutex_init(&port->port_lock, NULL, MUTEX_DRIVER, NULL); } /* * Function: * umad_release_hca_info * Input: * hca - hca info * Output: * Returns: * None * Called by: * - umad_init_hca_info in case of error * - umad_init_driver_info in case of error * - umad_context_destroyed in normal case * Description: * - For every port associated with this hca destory the mutex assicated * with the port and relese port info structure. * - Closes hca handle and resets the GUID */ static void umad_release_hca_info(umad_hca_info_t *hca) { unsigned int j; umad_port_info_t *port; #if defined(DEBUG) ibt_status_t rc; #endif if (hca->hca_ports) { for (j = 0; j < hca->hca_nports; j++) { port = &(hca->hca_ports[j]); if (port->port_num) mutex_destroy(&port->port_lock); } kmem_free(hca->hca_ports, hca->hca_nports * sizeof (umad_port_info_t)); hca->hca_ports = NULL; } if (hca->hca_handle) { #if defined(DEBUG) rc = ibt_close_hca(hca->hca_handle); if (rc != IBT_SUCCESS) { SOL_OFS_DPRINTF_L5(sol_umad_dbg_str, "umad_release_hca: ibt_close_hca() returned %d\n", rc); } #else (void) ibt_close_hca(hca->hca_handle); #endif hca->hca_handle = 0; } hca->hca_guid = 0; } /* * Function: * umad_init_hca_info * Input: * info pointer to umad info instructure * Output: * hca handle associated with this hca * Returns: * IBT_SUCCESS * IBT_HCA_IN_USE * IBT_HCA_INVALID * IBT_INVALID_PARAM * IBT_HCA_INVALID * Called by: * - umad_init_driver_info in case of error * Description: * - It calls ibt_open_hca to get handle associated wit this hca * - Determines how many port this hca has by calling ibt_query_hca * - Allocates space for each port associated with this hca. * - For every port it calls umad_init_port_info with the hca port * structure. * - It assigns port # index starting at 1 (1-N, zero is reserved, means * it does not exist). */ static int umad_init_hca_info(const umad_info_t *info, umad_hca_info_t *hca) { int rc; unsigned int j; umad_port_info_t *port; rc = ibt_open_hca(info->info_clnt_hdl, hca->hca_guid, &hca->hca_handle); if (rc != IBT_SUCCESS) goto error; rc = ibt_query_hca(hca->hca_handle, &hca->hca_attr); if (rc != IBT_SUCCESS) goto error; hca->hca_nports = hca->hca_attr.hca_nports; hca->hca_ports = kmem_zalloc(sizeof (umad_port_info_t) * hca->hca_nports, KM_SLEEP); /* Initialize ports structures. */ for (j = 0; j < hca->hca_nports; j++) { port = &hca->hca_ports[j]; umad_init_port_info(hca, port); /* * Note: A port number different than 0 means the port has been * initialized. */ port->port_num = j + 1; } error: if (rc) umad_release_hca_info(hca); return (rc); } /* * Function: * umad_init_driver_info * Output: * info - driver info * Returns: * IBT_SUCCESS * IBT_INVALID_PARAM * IBT_HCA_IN_USE * IBT_HCA_INVALID * IBT_INVALID_PARAM * Called by: * umad_attach * Description: * - Registers sol_umad instance with IBTF * - Calls ibt_get_hca_list to get hca count * - Allocates each hca and associate it with umad_info structure * - For every hca it assign GUID which was returned by ibt_get_hca_list * then calls umad_init_hca_info . * - Error case undone what was done, which calls umad_release_hca_info */ static ibt_status_t umad_init_driver_info(umad_info_t *info) { ibt_status_t rc; #if defined(DEBUG) ibt_status_t rc2; #endif unsigned int i; uint32_t hca_count; ib_guid_t *hca_guids = NULL; umad_hca_info_t *hca; info->info_hca_count = 0; info->info_clnt_hdl = NULL; info->info_hcas = NULL; rc = ibt_attach(&ibt_clnt_modinfo, info->info_dip, info, &info->info_clnt_hdl); if (rc != IBT_SUCCESS) goto err1; hca_count = info->info_hca_count = ibt_get_hca_list(&hca_guids); if (hca_count == 0) { rc = IBT_HCA_INVALID; goto err2; } info->info_hcas = kmem_zalloc(sizeof (umad_hca_info_t) * hca_count, KM_SLEEP); for (i = 0; i < hca_count; i++) { hca = &info->info_hcas[i]; /* Note: A non zero guid means the hca has been allocated. */ hca->hca_guid = hca_guids[i]; rc = umad_init_hca_info(info, hca); if (rc) goto err3; } ibt_free_hca_list(hca_guids, hca_count); return (0); err3: for (i = 0; i < info->info_hca_count; i++) { hca = &info->info_hcas[i]; if (hca->hca_guid) umad_release_hca_info(hca); } kmem_free(info->info_hcas, info->info_hca_count * sizeof (umad_hca_info_t)); info->info_hcas = NULL; if (hca_guids) ibt_free_hca_list(hca_guids, hca_count); err2: #if defined(DEBUG) rc2 = ibt_detach(info->info_clnt_hdl); if (rc2 != IBT_SUCCESS) { SOL_OFS_DPRINTF_L5(sol_umad_dbg_str, "umad_init_driver_info: ibt_detach failed: %d\n", rc2); } #else (void) ibt_detach(info->info_clnt_hdl); #endif info->info_clnt_hdl = NULL; err1: return (rc); } /* * Function: * umad_context_destroy * Input: * dip - device info * info - driver info * Output: * None * Returns: * None * Called by: * umad_attach * umad_detach * Description: * frees driver info resources */ static void umad_context_destroy(dev_info_t *dip, umad_info_t *info) { unsigned int i; unsigned int j; size_t n; for (i = 0; i < info->info_hca_count; i++) { umad_hca_info_t *hca = &info->info_hcas[i]; if (! hca->hca_guid) continue; for (j = 0; j < hca->hca_nports; j++) { umad_port_info_t *port = &hca->hca_ports[j]; char name[MAX_NAME_LEN]; if (port->port_has_umad_minor_node) { n = snprintf(name, sizeof (name), "umad%d", port->port_minor_name); #if defined(DEBUG) if (n > sizeof (name)) { SOL_OFS_DPRINTF_L5(sol_umad_dbg_str, "umad_context_destroy:" " minor name \"%s\": is longer than" " %d characters!\n", name, MAX_NAME_LEN); } #endif ddi_remove_minor_node(dip, name); } if (port->port_has_issm_minor_node) { n = snprintf(name, sizeof (name), "issm%d", port->port_minor_name); #if defined(DEBUG) if (n > sizeof (name)) { SOL_OFS_DPRINTF_L5(sol_umad_dbg_str, "umad_context_destroy:" " minor name \"%s\" is longer than" " %d characters!\n", name, MAX_NAME_LEN); } #endif ddi_remove_minor_node(dip, name); } } umad_release_hca_info(hca); } if (info->info_hcas) { kmem_free(info->info_hcas, info->info_hca_count * sizeof (umad_hca_info_t)); info->info_hca_count = 0; info->info_hcas = NULL; } if (info->info_clnt_hdl != NULL) { (void) ibt_detach(info->info_clnt_hdl); info->info_clnt_hdl = NULL; } mutex_destroy(&info->info_mutex); } /* * Function: * _init * Input: * None * Output: * None * Returns: * status * Called by: * Framework * Description: * driver initialization function * inits debug tracing, river info and calls mod_install */ int _init(void) { int rc; rc = ddi_soft_state_init(&umad_statep, sizeof (umad_info_t), 0); if (rc != 0) goto err; rc = mod_install(&modlinkage); if (rc != 0) ddi_soft_state_fini(&umad_statep); err: return (rc); } /* * Function: * _info * Input: * None * Output: * modinfop Module information * Returns: * status * Called by: * Framework * Description: * Provides module information */ int _info(struct modinfo *modinfop) { int rc; rc = mod_info(&modlinkage, modinfop); return (rc); } /* * Function: * _fini * Input: * None * Output: * None * Returns: * status * Called by: * Framework * Description: * Cleans up upon module unloading */ int _fini(void) { int rc; if ((rc = mod_remove(&modlinkage)) == 0) ddi_soft_state_fini(&umad_statep); return (rc); } /* * Function: * umad_attach * Input: * dip device info * cmd DDI_ATTACH all others are invalid * Output: * None * Returns: * DDI_SUCCESS or DDI_FAILURE * Called by: * Framwork * Description: * Device attach routine */ static int umad_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) { int rc; unsigned int i; unsigned int j; umad_hca_info_t hca; umad_info_t *info; char name[MAX_NAME_LEN]; unsigned int minor_name; switch (cmd) { case DDI_ATTACH: if (ddi_soft_state_zalloc(umad_statep, UMAD_INSTANCE) != DDI_SUCCESS) goto err1; info = ddi_get_soft_state(umad_statep, UMAD_INSTANCE); if (info == NULL) goto err2; info->info_dip = dip; mutex_init(&info->info_mutex, NULL, MUTEX_DRIVER, NULL); /* initialize our data and per HCA info */ rc = umad_init_driver_info(info); if (rc != 0) goto err3; rc = ddi_prop_update_int(DDI_DEV_T_NONE, dip, "abi_version", IB_USER_MAD_ABI_VERSION); if (rc != 0) goto err3; /* * create a minor node for each node/port pair * device names are consistent with OFA * conventions, e.g. umad0 for port 1 on the first HCA. */ minor_name = 0; for (i = 0; i < info->info_hca_count; i++) { hca = info->info_hcas[i]; for (j = 0; j < hca.hca_nports; j++) { size_t n; dev_t minor_dev; umad_port_info_t *port = &hca.hca_ports[j]; port->port_minor_name = minor_name; n = snprintf(name, sizeof (name), "umad%d", minor_name); #if defined(DEBUG) if (n > sizeof (name)) { SOL_OFS_DPRINTF_L5(sol_umad_dbg_str, "umad_attach: " "name \"%s\" longer than %d!\n", name, MAX_NAME_LEN); } #endif rc = ddi_create_minor_node(dip, name, S_IFCHR, GET_UMAD_MINOR(i, j), DDI_PSEUDO, 0); if (rc != DDI_SUCCESS) goto err3; minor_dev = makedevice(ddi_driver_major(dip), GET_UMAD_MINOR(i, j)); rc = ddi_prop_update_int(minor_dev, dip, "vendor-id", hca.hca_attr.hca_vendor_id); if (rc != DDI_SUCCESS) goto err3; rc = ddi_prop_update_int(minor_dev, dip, "device-id", hca.hca_attr.hca_device_id); if (rc != DDI_SUCCESS) goto err3; rc = ddi_prop_update_int(minor_dev, dip, "hca-instance", i); if (rc != DDI_SUCCESS) goto err3; rc = ddi_prop_update_int(minor_dev, dip, "port", j + 1); if (rc != DDI_SUCCESS) goto err3; port->port_has_umad_minor_node = 1; n = snprintf(name, sizeof (name), "issm%d", minor_name); #if defined(DEBUG) if (n > sizeof (name)) { SOL_OFS_DPRINTF_L5(sol_umad_dbg_str, "umad_attach: " "name \"%s\" longer than %d!\n", name, MAX_NAME_LEN); } #endif rc = ddi_create_minor_node(dip, name, S_IFCHR, GET_ISSM_MINOR(i, j), DDI_PSEUDO, 0); if (rc != DDI_SUCCESS) goto err3; minor_dev = makedevice(ddi_driver_major(dip), GET_ISSM_MINOR(i, j)); rc = ddi_prop_update_int(minor_dev, dip, "vendor-id", hca.hca_attr.hca_vendor_id); if (rc != DDI_SUCCESS) goto err3; rc = ddi_prop_update_int(minor_dev, dip, "device-id", hca.hca_attr.hca_device_id); if (rc != DDI_SUCCESS) goto err3; rc = ddi_prop_update_int(minor_dev, dip, "hca-instance", i); if (rc != DDI_SUCCESS) goto err3; rc = ddi_prop_update_int(minor_dev, dip, "port", j + 1); if (rc != DDI_SUCCESS) goto err3; port->port_has_issm_minor_node = 1; minor_name++; } } ddi_report_dev(dip); break; default: goto err1; } rc = DDI_SUCCESS; return (rc); err3: umad_context_destroy(dip, info); err2: ddi_soft_state_free(umad_statep, UMAD_INSTANCE); err1: rc = DDI_FAILURE; return (rc); } /* * Function: * umad_detach * Input: * dip Device pointer * cmd DDI_DETACH all others are an error * Output: * None * Returns: * DDI_SUCCESS or DDI_FAILURE * Called by: * Framework * Description: * Used when a device is removed */ static int umad_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) { int rc = DDI_SUCCESS; umad_info_t *info; switch (cmd) { case DDI_DETACH: info = ddi_get_soft_state(umad_statep, UMAD_INSTANCE); umad_context_destroy(dip, info); ddi_soft_state_free(umad_statep, UMAD_INSTANCE); break; default: rc = DDI_FAILURE; break; } return (rc); } /* * Function: * umad_getinfo * Input: * dip device pointer * cmd DDI_INFO_DEVT2DEVINFO or DDI_INFO_DEV2INSTANCE * arg Unused * Output: * resultp device pointer or device instance as per cmd * Returns: * status * Called by: * Framework * Description: * Gets information about specific device */ static int umad_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp) { int rc; #if defined(__lint) extern void dummy2(void *); dummy2(arg); #endif switch (cmd) { case DDI_INFO_DEVT2DEVINFO: *resultp = (void *)dip; break; case DDI_INFO_DEVT2INSTANCE: *resultp = (void *)UMAD_INSTANCE; rc = DDI_SUCCESS; break; default: rc = DDI_FAILURE; break; } return (rc); } /* * Function: * umad_prop_op * Input: * dev device * dip device pointer * prop_op which property operation * flags property flags * name proper name * Output: * valuep - property value * lengthp - propery length * Returns: * status * Called by: * Framework * Description: * Passes straight through to default ddi_prop_op() */ static int umad_prop_op( dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op, int flags, char *name, caddr_t valuep, int *lengthp) { int rc; rc = ddi_prop_op(dev, dip, prop_op, flags, name, valuep, lengthp); return (rc); } /* Returns an array of mad classes associated with IBMF class */ static const uint8_t * umad_get_mad_classes_by_ibmf_class(enum _ibmf_client_type_t ibmf_class) { const struct ibmf_class_to_mad_type *entry; for (entry = &ibmf_class_to_mad_types[0]; entry->ibmf_class != 0; ++entry) { if (ibmf_class == entry->ibmf_class) return (entry->mad_types); } return (NULL); } /* Returns an agent from its ID. */ static umad_agent_t * umad_get_agent_by_id(umad_uctx_t *uctx, uint32_t agent_id) { umad_agent_t *agent; llist_head_t *entry; ASSERT(MUTEX_HELD(&uctx->uctx_lock)); /* Look for the agent */ list_for_each(entry, &uctx->uctx_agent_list) { agent = entry->ptr; if (agent_id == agent->agent_req.id) return (agent); } return (NULL); } /* Returns an agent from its MAD class. */ static umad_agent_t * umad_get_agent_by_class(umad_uctx_t *uctx, uint8_t agent_class) { umad_agent_t *agent; llist_head_t *entry; ASSERT(MUTEX_HELD(&uctx->uctx_lock)); /* Look for the agent */ list_for_each(entry, &uctx->uctx_agent_list) { agent = entry->ptr; if (agent_class == agent->agent_req.mgmt_class) return (agent); } return (NULL); } /* * Register the agent with a class. * mgmt_class is given from userspace. */ static int umad_register_agent(struct umad_agent_s *agent) { uint8_t mgmt_class_num = agent->agent_req.mgmt_class; umad_port_info_t *port = agent->agent_uctx->uctx_port; const umad_hca_info_t *hca = port->port_hca; int rc; ibmf_register_info_t reg_info = {0, }; ibmf_impl_caps_t impl_caps = {0, }; uint_t flags = 0; enum _ibmf_client_type_t ibmf_class; const uint8_t *umad_types; struct ibmf_reg_info *ibmf_info; llist_head_t *entry; boolean_t found = B_FALSE; ASSERT(MUTEX_HELD(&agent->agent_uctx->uctx_lock)); /* * Map MAD class to IBMF class */ ibmf_class = umad_type_to_ibmf_class[mgmt_class_num]; /* * It is is reserved, bail */ if (ibmf_class == 0) { rc = EINVAL; goto done; } /* Check to see if any other mad classes also map to this IBMF class */ umad_types = umad_get_mad_classes_by_ibmf_class(ibmf_class); if (umad_types != NULL) { struct umad_agent_s *other_agent; for (; *umad_types != 0; ++umad_types) { other_agent = umad_get_agent_by_class(agent->agent_uctx, *umad_types); if (other_agent != NULL) { struct ibmf_reg_info *ibmf_reg; ibmf_reg = other_agent->agent_reg; agent->agent_reg = ibmf_reg; if (other_agent->agent_flags & UMAD_HANDLING_ASYNC) { agent->agent_flags |= UMAD_HANDLING_ASYNC; } mutex_enter(&ibmf_reg->ibmf_reg_lock); while (ibmf_reg->ibmf_flags & UMAD_IBMF_UNREGISTERING) { cv_wait(&ibmf_reg->ibmf_cv, &ibmf_reg->ibmf_reg_lock); } ibmf_reg->ibmf_reg_refcnt++; mutex_exit(&ibmf_reg->ibmf_reg_lock); return (0); } } } /* * At this point we need to check if there is already an * ibmf_info already associated with this HCA, port and ibmf * class. If so, simply increment the reference count * and set the agent's agent_reg field to point to the * ibmf_info structure that was found. (under locking) */ mutex_enter(&port->port_lock); if (! llist_empty(&port->port_ibmf_regs)) { list_for_each(entry, &port->port_ibmf_regs) { ibmf_info = (struct ibmf_reg_info *)entry->ptr; if (ibmf_info->ibmf_class == ibmf_class) { found = B_TRUE; break; } } } mutex_exit(&port->port_lock); if (found) { mutex_enter(&ibmf_info->ibmf_reg_lock); ibmf_info->ibmf_reg_refcnt++; agent->agent_reg = ibmf_info; mutex_exit(&ibmf_info->ibmf_reg_lock); return (0); } ibmf_info = kmem_zalloc(sizeof (struct ibmf_reg_info), KM_SLEEP); mutex_init(&ibmf_info->ibmf_reg_lock, NULL, MUTEX_DRIVER, NULL); cv_init(&ibmf_info->ibmf_cv, NULL, CV_DRIVER, NULL); if (agent->agent_req.rmpp_version) flags = IBMF_REG_FLAG_RMPP; reg_info.ir_ci_guid = hca->hca_guid; reg_info.ir_port_num = port->port_num; reg_info.ir_client_class = ibmf_class; mutex_enter(&ibmf_info->ibmf_reg_lock); rc = ibmf_register(®_info, IBMF_VERSION, flags, NULL, NULL, &ibmf_info->ibmf_reg_handle, &impl_caps); if (rc != IBMF_SUCCESS) { mutex_exit(&ibmf_info->ibmf_reg_lock); kmem_free(ibmf_info, sizeof (*ibmf_info)); } else { /* The client wants to receive some unsolicited MADs. */ rc = ibmf_setup_async_cb(ibmf_info->ibmf_reg_handle, IBMF_QP_HANDLE_DEFAULT, umad_unsolicited_cb, (void *)ibmf_info, 0); if (rc != IBMF_SUCCESS) { (void) ibmf_unregister(&ibmf_info->ibmf_reg_handle, 0); mutex_exit(&ibmf_info->ibmf_reg_lock); kmem_free(ibmf_info, sizeof (*ibmf_info)); } else { ibmf_info->ibmf_reg_refcnt++; ibmf_info->ibmf_reg_uctx = agent->agent_uctx; ibmf_info->ibmf_class = ibmf_class; agent->agent_reg = ibmf_info; agent->agent_flags |= UMAD_HANDLING_ASYNC; mutex_exit(&ibmf_info->ibmf_reg_lock); entry = kmem_zalloc(sizeof (llist_head_t), KM_SLEEP); entry->ptr = ibmf_info; mutex_enter(&port->port_lock); llist_add(entry, &port->port_ibmf_regs); mutex_exit(&port->port_lock); } } done: return (rc); } /* * Function: * umad_queue_mad_msg * Input: * port - handle to ibmf * ibmf_msg - The incoming SM MAD * Output: * None * Returns: * 0 on success, otherwise error number * Called by: * umad_solicitied_cb and umad_unsolicited_cb * Description: * creates a umad_msg and adds it to the appropriate user's context */ static int umad_queue_mad_msg(struct umad_agent_s *agent, ibmf_msg_t *ibmf_msg) { int rc; ib_umad_msg_t *umad_msg; umad_uctx_t *uctx = agent->agent_uctx; if (agent->agent_uctx == NULL) { rc = ENOENT; goto err1; } umad_msg = kmem_zalloc(sizeof (*umad_msg), KM_NOSLEEP); if (umad_msg == NULL) { rc = ENOMEM; goto err1; } umad_msg->umad_msg_hdr.id = agent->agent_req.id; umad_msg->umad_msg_hdr.status = ibmf_msg->im_msg_status; umad_msg->umad_msg_hdr.length = IB_MGMT_MAD_HDR + ibmf_msg->im_msgbufs_recv.im_bufs_cl_hdr_len + ibmf_msg->im_msgbufs_recv.im_bufs_cl_data_len; umad_msg->umad_msg_hdr.qpn = htonl(ibmf_msg->im_local_addr.ia_remote_qno); umad_msg->umad_msg_hdr.lid = htons(ibmf_msg->im_local_addr.ia_remote_lid); umad_msg->umad_msg_hdr.sl = htonl(ibmf_msg->im_local_addr.ia_service_level); umad_msg->umad_msg_ibmf_msg = ibmf_msg; mutex_enter(&uctx->uctx_recv_lock); if (! add_genlist(&uctx->uctx_recv_list, (uintptr_t)umad_msg, agent)) { kmem_free(umad_msg, sizeof (*umad_msg)); mutex_exit(&uctx->uctx_recv_lock); rc = ENOMEM; goto err1; } mutex_exit(&uctx->uctx_recv_lock); cv_broadcast(&uctx->uctx_recv_cv); pollwakeup(&uctx->uctx_pollhead, POLLIN | POLLRDNORM); rc = 0; err1: return (rc); } /* Frees up user context state */ static void umad_release_uctx(umad_uctx_t *uctx) { ASSERT(genlist_empty(&uctx->uctx_recv_list)); ASSERT(llist_empty(&uctx->uctx_agent_list)); cv_destroy(&uctx->uctx_recv_cv); mutex_destroy(&uctx->uctx_lock); mutex_destroy(&uctx->uctx_recv_lock); } /* * Function: * umad_open * Input: * devp device pointer * flag Unused * otyp Open type (just validated) * cred Unused * Output: * None * Returns: * status * Called by: * Device open framework * Description: * If this is the issm device, modify the port to indicate that this is * a subnet manager. If regular umad device, allocate and initialize * a new user context and connect it to the hca info. Return the new * dev_t for the new minor. */ static int umad_open(dev_t *dev, int flag, int otyp, cred_t *cred) { umad_info_t *info; minor_t minor; minor_t ctx_minor; int node_id, port_num; int rc = DDI_SUCCESS; umad_hca_info_t *hca; umad_port_info_t *port; umad_uctx_t *uctx; #if defined(__lint) extern void dummy(int); dummy(flag); #endif rc = priv_policy(cred, PRIV_SYS_NET_CONFIG, B_FALSE, EACCES, NULL); if (rc != 0) return (rc); info = ddi_get_soft_state(umad_statep, UMAD_INSTANCE); if (info == NULL) { rc = ENXIO; goto err1; } if (otyp != OTYP_CHR) return (EINVAL); /* lookup the node and port #s */ minor = getminor(*dev); node_id = GET_NODE(minor); port_num = GET_PORT(minor); hca = &info->info_hcas[node_id]; port = &hca->hca_ports[port_num]; if (ISSM_MINOR(minor)) { ibt_status_t rc; mutex_enter(&port->port_lock); if (port->port_issm_open_cnt) { mutex_exit(&port->port_lock); rc = EBUSY; goto err1; } port->port_issm_open_cnt++; mutex_exit(&port->port_lock); rc = ibt_modify_port(hca->hca_handle, port->port_num, IBT_PORT_SET_SM, 0); if (rc) { mutex_enter(&port->port_lock); port->port_issm_open_cnt--; mutex_exit(&port->port_lock); goto err1; } } else { unsigned int uctx_num; uctx = kmem_zalloc(sizeof (umad_uctx_t), KM_SLEEP); mutex_init(&uctx->uctx_lock, NULL, MUTEX_DRIVER, NULL); cv_init(&uctx->uctx_recv_cv, NULL, CV_DRIVER, NULL); init_genlist(&uctx->uctx_recv_list); mutex_init(&uctx->uctx_recv_lock, NULL, MUTEX_DRIVER, NULL); llist_head_init(&uctx->uctx_agent_list, NULL); uctx->uctx_port = port; mutex_enter(&info->info_mutex); mutex_enter(&port->port_lock); /* Find a free entry in uctx list */ for (uctx_num = 0; uctx_num < MAX_UCTX; uctx_num++) { if (info->info_uctx[uctx_num] == NULL) break; } if (uctx_num == MAX_UCTX) { /* No room found */ mutex_exit(&port->port_lock); mutex_exit(&info->info_mutex); umad_release_uctx(uctx); rc = EBUSY; goto err1; } ctx_minor = GET_NEW_UCTX_MINOR(minor, uctx_num); info->info_uctx[uctx_num] = uctx; *dev = makedevice(getmajor(*dev), ctx_minor); mutex_exit(&port->port_lock); mutex_exit(&info->info_mutex); } err1: return (rc); } /* * Function: * umad_close * Input: * dev device * flag Unused * otyp Unused * cred Unused * Output: * None * Returns: * status * Called by: * Device close framework * Description: * Unwinds open while waiting for any pending I/O to complete. */ /* ARGSUSED1 */ static int umad_close(dev_t dev, int flag, int otyp, cred_t *cred) { umad_info_t *info; minor_t minor; int rc = DDI_SUCCESS; umad_port_info_t *port; umad_uctx_t *uctx; llist_head_t *lentry; llist_head_t *lentry_temp; umad_agent_t *agent; int port_num; umad_hca_info_t *hca; int node_id; info = ddi_get_soft_state(umad_statep, UMAD_INSTANCE); if (info == NULL) { rc = ENXIO; goto err1; } minor = getminor(dev); node_id = GET_NODE(minor); port_num = GET_PORT(minor); hca = &info->info_hcas[node_id]; port = &hca->hca_ports[port_num]; ASSERT(port != NULL); if (ISSM_MINOR(minor)) { (void) ibt_modify_port(hca->hca_handle, port->port_num, IBT_PORT_RESET_SM, 0); mutex_enter(&port->port_lock); port->port_issm_open_cnt--; mutex_exit(&port->port_lock); ASSERT(port->port_issm_open_cnt == 0); } else { mutex_enter(&info->info_mutex); uctx = info->info_uctx[GET_UCTX(minor)]; ASSERT(uctx != NULL); mutex_enter(&uctx->uctx_lock); /* Unregister the agents. Cancel the pending operations. */ lentry = uctx->uctx_agent_list.nxt; lentry_temp = lentry->nxt; while (lentry != &uctx->uctx_agent_list) { ASSERT(lentry); agent = lentry->ptr; (void) umad_unregister(&agent->agent_req, uctx); lentry = lentry_temp; lentry_temp = lentry->nxt; } mutex_exit(&uctx->uctx_lock); umad_release_uctx(uctx); kmem_free(uctx, sizeof (umad_uctx_t)); info->info_uctx[GET_UCTX(minor)] = NULL; mutex_exit(&info->info_mutex); } err1: return (rc); } /* * return where optional header starts relative to the start * of the transmited mad */ static int umad_get_mad_clhdr_offset(uint8_t mgmt_class) { switch (mgmt_class) { case MAD_MGMT_CLASS_SUBN_LID_ROUTED: case MAD_MGMT_CLASS_SUBN_DIRECT_ROUTE: case MAD_MGMT_CLASS_PERF: case MAD_MGMT_CLASS_BM: case MAD_MGMT_CLASS_DEV_MGT: case MAD_MGMT_CLASS_COMM_MGT: return (IB_MGMT_MAD_HDR); case MAD_MGMT_CLASS_SUBN_ADM: return (IB_MGMT_RMPP_HDR); case MAD_MGMT_CLASS_SNMP: return (IB_MGMT_SNMP_HDR); default: if (((mgmt_class >= MAD_MGMT_CLASS_VENDOR_START) && (mgmt_class <= MAD_MGMT_CLASS_VENDOR_END)) || ((mgmt_class >= MAD_MGMT_CLASS_APPLICATION_START) && (mgmt_class <= MAD_MGMT_CLASS_APPLICATION_END))) return (IB_MGMT_MAD_HDR); else if ((mgmt_class >= MAD_MGMT_CLASS_VENDOR2_START) && (mgmt_class <= MAD_MGMT_CLASS_VENDOR2_END)) return (IB_MGMT_RMPP_HDR); else { #if defined(DEBUG) SOL_OFS_DPRINTF_L5(sol_umad_dbg_str, "umad_get_mad_clhdr_offset:" " got illegal management class %d", mgmt_class); #endif return (0); /* invalid mad */ } } } /* * return the offset of the mad data in the transmited mad * following all headers */ static int umad_get_mad_data_offset(uint8_t mgmt_class) { switch (mgmt_class) { case MAD_MGMT_CLASS_SUBN_LID_ROUTED: case MAD_MGMT_CLASS_SUBN_DIRECT_ROUTE: case MAD_MGMT_CLASS_PERF: case MAD_MGMT_CLASS_BM: case MAD_MGMT_CLASS_DEV_MGT: case MAD_MGMT_CLASS_COMM_MGT: return (IB_MGMT_MAD_HDR); case MAD_MGMT_CLASS_SUBN_ADM: return (IB_MGMT_SA_HDR); case MAD_MGMT_CLASS_SNMP: return (IB_MGMT_SNMP_DATA); default: if (((mgmt_class >= MAD_MGMT_CLASS_VENDOR_START) && (mgmt_class <= MAD_MGMT_CLASS_VENDOR_END)) || ((mgmt_class >= MAD_MGMT_CLASS_APPLICATION_START) && (mgmt_class <= MAD_MGMT_CLASS_APPLICATION_END))) return (IB_MGMT_MAD_HDR); else if ((mgmt_class >= MAD_MGMT_CLASS_VENDOR2_START) && (mgmt_class <= MAD_MGMT_CLASS_VENDOR2_END)) return (IB_MGMT_VENDOR_HDR); else { #if defined(DEBUG) SOL_OFS_DPRINTF_L5(sol_umad_dbg_str, "umad_get_mad_clhdr_offset:" " got illegal management class %d", mgmt_class); #endif return (0); /* invalid mad */ } } } /* * Function: * umad_read * Input: * dev device * uiop User I/O pointer * credp Unused * Output: * None * Returns: * status * Called by: * Device read framework * Description: * Cannot read from ISSM device. Read from UMAD device * does usual checks for blocking and when data is present, * removes message from user context receive list, fills in user * space with message and frees kernel copy of the message. */ /* ARGSUSED2 */ static int umad_read(dev_t dev, struct uio *uiop, cred_t *credp) { int minor; size_t data_len; int rc = 0; umad_port_info_t *port; umad_info_t *info; umad_uctx_t *uctx; genlist_entry_t *entry; ib_umad_msg_t *umad_msg; ibmf_msg_t *ibmf_msg; struct umad_agent_s *agent; ib_mad_hdr_t *ib_mad_hdr; ssize_t start_resid; info = ddi_get_soft_state(umad_statep, UMAD_INSTANCE); if (info == NULL) { rc = ENXIO; goto err1; } minor = getminor(dev); if (ISSM_MINOR(minor)) { rc = ENXIO; goto err1; } mutex_enter(&info->info_mutex); uctx = info->info_uctx[GET_UCTX(minor)]; mutex_exit(&info->info_mutex); ASSERT(uctx != NULL); port = uctx->uctx_port; ASSERT(port != NULL); start_resid = uiop->uio_resid; while (rc == 0 && uiop->uio_resid > 0) { mutex_enter(&uctx->uctx_recv_lock); /* Check to see if we are in blocking mode or not */ if (! (uiop->uio_fmode & (FNDELAY | FNONBLOCK))) { while (genlist_empty(&uctx->uctx_recv_list)) { if (cv_wait_sig(&uctx->uctx_recv_cv, &uctx->uctx_recv_lock) == 0) { mutex_exit(&uctx->uctx_recv_lock); return (EINTR); } } } else if (genlist_empty(&uctx->uctx_recv_list)) { mutex_exit(&uctx->uctx_recv_lock); /* Check for a short read */ if (uiop->uio_resid != start_resid) return (0); return (EAGAIN); } entry = remove_genlist_head(&uctx->uctx_recv_list); mutex_exit(&uctx->uctx_recv_lock); ASSERT(entry != NULL); agent = entry->data_context; umad_msg = (ib_umad_msg_t *)entry->data; ibmf_msg = (ibmf_msg_t *)umad_msg->umad_msg_ibmf_msg; data_len = min(uiop->uio_resid, sizeof (struct ib_user_mad)); rc = uiomove(umad_msg, data_len, UIO_READ, uiop); if (rc) goto err2; if (ibmf_msg->im_msg_status == IBMF_SUCCESS) { ib_mad_hdr = (ib_mad_hdr_t *) ibmf_msg->im_msgbufs_recv.im_bufs_mad_hdr; data_len = umad_get_mad_clhdr_offset(ib_mad_hdr->MgmtClass); data_len = min(uiop->uio_resid, data_len); rc = uiomove(ibmf_msg->im_msgbufs_recv.im_bufs_mad_hdr, data_len, UIO_READ, uiop); if (rc) goto err2; data_len = min(uiop->uio_resid, ibmf_msg->im_msgbufs_recv.im_bufs_cl_hdr_len); rc = uiomove(ibmf_msg->im_msgbufs_recv.im_bufs_cl_hdr, data_len, UIO_READ, uiop); if (rc) goto err2; data_len = min(uiop->uio_resid, ibmf_msg->im_msgbufs_recv.im_bufs_cl_data_len); rc = uiomove(ibmf_msg->im_msgbufs_recv.im_bufs_cl_data, data_len, UIO_READ, uiop); if (rc) goto err2; } rc = ibmf_free_msg(agent->agent_reg->ibmf_reg_handle, &ibmf_msg); kmem_free(umad_msg, sizeof (*umad_msg)); if (rc != IBMF_SUCCESS) { #if defined(DEBUG) SOL_OFS_DPRINTF_L5(sol_umad_dbg_str, "umad_read:" " ibmf_free_msg failed %d", rc); #endif goto err1; } } err2: if (rc) { rc = ibmf_free_msg(agent->agent_reg->ibmf_reg_handle, &ibmf_msg); kmem_free(umad_msg, sizeof (*umad_msg)); if (rc != IBMF_SUCCESS) { #if defined(DEBUG) SOL_OFS_DPRINTF_L5(sol_umad_dbg_str, "umad_read:" " ibmf_free_msg failed %d", rc); #endif } } err1: return (rc); } /* * Function: * umad_solicited_cb * Input: * ibmf_handle - handle to ibmf * msgp - The incoming SM MAD * args - umad_port_info_t object that the MAD cam in on * Output: * None * Returns: * none * Called by: * Description: * Callback function (ibmf_msg_cb_t) that is invoked when the * ibmf receives a SM MAD for the given Port. * This function copies the MAD into the port recv queue. */ static void umad_solicited_cb(ibmf_handle_t ibmf_handle, ibmf_msg_t *msgp, void *args) { struct umad_send *umad_ctx = (struct umad_send *)args; umad_agent_t *agent = umad_ctx->send_agent; int rc; #if defined(__lint) ibmf_handle = 0; #endif msgp->im_msgbufs_send.im_bufs_mad_hdr = NULL; msgp->im_msgbufs_send.im_bufs_cl_hdr = NULL; msgp->im_msgbufs_send.im_bufs_cl_hdr_len = 0; msgp->im_msgbufs_send.im_bufs_cl_data = NULL; msgp->im_msgbufs_send.im_bufs_cl_data_len = 0; kmem_free(umad_ctx, umad_ctx->send_len); mutex_enter(&agent->agent_lock); agent->agent_outstanding_msgs--; ASSERT(agent->agent_outstanding_msgs >= 0); if (agent->agent_flags & UMAD_AGENT_UNREGISTERING) { if (agent->agent_outstanding_msgs == 0) cv_signal(&agent->agent_cv); } mutex_exit(&agent->agent_lock); if (umad_queue_mad_msg(agent, msgp)) goto bad; return; bad: rc = ibmf_free_msg(agent->agent_reg->ibmf_reg_handle, &msgp); ASSERT(rc == IBMF_SUCCESS); } /* * Function: * umad_write * Input: * dev device * uiop User I/O pointer * credp Unused * Output: * None * Returns: * status * Called by: * Device write framework * Description: * Cannot write to ISSM device. Allocate new umad_send structure * and ibmf message and copy from user space into allocated message. * Fill in required fields. If this is a request make sure * umad_solicited_cb() is passed. */ /* ARGSUSED1 */ static int umad_write(dev_t dev, struct uio *uiop, cred_t *credp) { int rc, rc2; int mad_offset, flags = 0; int hdr_len; size_t len = uiop->uio_resid; minor_t minor; ibmf_retrans_t mad_retrans; umad_info_t *info; umad_port_info_t *port; umad_uctx_t *uctx; umad_agent_t *agent; struct ib_user_mad *user_mad; /* incoming uMAD hdr */ ibmf_msg_t *ibmf_msg; /* outbound MAD mesg */ ib_mad_hdr_t *ib_mad_hdr; /* outbound MAD hdrs */ struct umad_send *umad_ctx; boolean_t need_callback; ibt_status_t status; ib_pkey_t pkey; info = ddi_get_soft_state(umad_statep, UMAD_INSTANCE); if (info == NULL) { rc = ENXIO; goto err1; } /* lookup the node and port #s */ minor = getminor(dev); if (ISSM_MINOR(minor)) { rc = ENXIO; goto err1; } mutex_enter(&info->info_mutex); uctx = info->info_uctx[GET_UCTX(minor)]; mutex_exit(&info->info_mutex); ASSERT(uctx != NULL); port = uctx->uctx_port; ASSERT(port != NULL); umad_ctx = kmem_zalloc(sizeof (struct umad_send) + len, KM_SLEEP); umad_ctx->send_len = sizeof (struct umad_send) + len; /* copy the MAD data in from user space */ /* data = user_mad + mad_hdrs + class_hdrs + class data */ /* LINTED */ user_mad = (struct ib_user_mad *)umad_ctx->send_umad; rc = uiomove(user_mad, len, UIO_WRITE, uiop); if (rc != 0) goto err3; /* Look for the agent */ mutex_enter(&uctx->uctx_lock); agent = umad_get_agent_by_id(uctx, user_mad->hdr.id); mutex_exit(&uctx->uctx_lock); if (! agent) { rc = EINVAL; goto err3; } mutex_enter(&agent->agent_lock); if (agent->agent_flags & UMAD_AGENT_UNREGISTERING) { mutex_exit(&agent->agent_lock); rc = EINVAL; goto err3; } /* Allocate the msg buf for IBMF */ rc = ibmf_alloc_msg(agent->agent_reg->ibmf_reg_handle, IBMF_ALLOC_NOSLEEP, &ibmf_msg); if (rc != IBMF_SUCCESS) { mutex_exit(&agent->agent_lock); goto err3; } ib_mad_hdr = (ib_mad_hdr_t *)user_mad->data; hdr_len = umad_get_mad_data_offset(ib_mad_hdr->MgmtClass); /* * build the IBMF msg from the mad data passed in * construct the addr info */ #if defined(__FUTURE_FEATURE__) /* TODO Proper GRH handling (non-smp traffic only) */ if (mad.addr.grh_present) { memcpy(&ibmf_msg->im_global_addr.ig_recver_gid, mad.addr.gid, 16); // where can we get the GID?? im_global_addr.ig_sender_gid = get_gid(umad->addr.gid_index); ibmf_msg->im_global_addr.ig_tclass = mad.addr.traffic_class; ibmf_msg->im_global_addr.ig_hop_limit = mad.addr.hop_limit; ibmf_msg->im_global_addr.ig_flow_label = mad.addr.flow_label; } #endif /* * Note: umad lid, qpn and qkey are in network order, so we need * to revert them to give them to ibmf. See userspace * umad_set_addr() and umad_set_addr_net(). */ ibmf_msg->im_local_addr.ia_local_lid = port->port_lid; ibmf_msg->im_local_addr.ia_remote_lid = ntohs(user_mad->hdr.lid); ibmf_msg->im_local_addr.ia_remote_qno = ntohl(user_mad->hdr.qpn); ibmf_msg->im_local_addr.ia_q_key = ntohl(user_mad->hdr.qkey); ibmf_msg->im_local_addr.ia_service_level = user_mad->hdr.sl; status = ibt_index2pkey(port->port_hca->hca_handle, port->port_num, user_mad->hdr.pkey_index, &pkey); if (status != IBT_SUCCESS) { #if defined(DEBUG) SOL_OFS_DPRINTF_L5(sol_umad_dbg_str, "umad_write: ibt_index2pkey failed %d", status); #endif } else ibmf_msg->im_local_addr.ia_p_key = ntohs(pkey); if ((ib_mad_hdr->R_Method & 0x80) == 0) flags = IBMF_MSG_TRANS_FLAG_SEQ; /* * This code is only correct for the cases of * no headers beyond the MAD header or the case of * MAD_MGMT_CLASS_SUBN_ADM (SA type) which has both * an RMPP header and an SA header. Other header combinations * are simply not dealt with correctly, but no applications * utilize them either, so we should be ok. */ /* set use RMPP if UserAgent registered for it */ if (agent->agent_req.rmpp_version > 0) { ibmf_rmpp_hdr_t *rmpp_hdr; rmpp_hdr = (ibmf_rmpp_hdr_t *)(ib_mad_hdr + 1); if (rmpp_hdr->rmpp_flags != 0) flags |= IBMF_MSG_TRANS_FLAG_RMPP; } /* construct the msg bufs */ ibmf_msg->im_msgbufs_send.im_bufs_mad_hdr = ib_mad_hdr; hdr_len = umad_get_mad_clhdr_offset(ib_mad_hdr->MgmtClass); mad_offset = umad_get_mad_data_offset(ib_mad_hdr->MgmtClass); /* Class headers and len, rmpp? */ ibmf_msg->im_msgbufs_send.im_bufs_cl_hdr = (unsigned char *)user_mad + offsetof(struct ib_user_mad, data) + hdr_len; ibmf_msg->im_msgbufs_send.im_bufs_cl_hdr_len = mad_offset - hdr_len; ibmf_msg->im_msgbufs_send.im_bufs_cl_data = (unsigned char *) user_mad + (sizeof (struct ib_user_mad) + mad_offset); ibmf_msg->im_msgbufs_send.im_bufs_cl_data_len = len - sizeof (struct ib_user_mad) - mad_offset; mad_retrans.retrans_retries = user_mad->hdr.retries; mad_retrans.retrans_rtv = 0; mad_retrans.retrans_rttv = 0; mad_retrans.retrans_trans_to = 0; umad_ctx->send_agent = agent; need_callback = (flags & IBMF_MSG_TRANS_FLAG_SEQ) != 0; if (need_callback) agent->agent_outstanding_msgs++; mutex_exit(&agent->agent_lock); /* pass the MAD down to the IBMF layer */ rc = ibmf_msg_transport(agent->agent_reg->ibmf_reg_handle, IBMF_QP_HANDLE_DEFAULT, ibmf_msg, &mad_retrans, need_callback ? umad_solicited_cb : NULL, umad_ctx, flags); if (! need_callback) { rc2 = ibmf_free_msg(agent->agent_reg->ibmf_reg_handle, &ibmf_msg); ASSERT(rc2 == IBMF_SUCCESS); if (rc != IBMF_SUCCESS) { rc = EIO; goto err3; } } else if (rc != IBMF_SUCCESS) { mutex_enter(&agent->agent_lock); agent->agent_outstanding_msgs--; ASSERT(agent->agent_outstanding_msgs >= 0); if (agent->agent_flags & UMAD_AGENT_UNREGISTERING) { if (agent->agent_outstanding_msgs == 0) cv_signal(&agent->agent_cv); } mutex_exit(&agent->agent_lock); rc2 = ibmf_free_msg(agent->agent_reg->ibmf_reg_handle, &ibmf_msg); ASSERT(rc2 == IBMF_SUCCESS); rc = EIO; goto err3; } return (0); err3: kmem_free(umad_ctx, umad_ctx->send_len); err1: return (rc); } /* * Function: * umad_async_handler * Input: * private Unused * hca_hdl Unused * code Unused * event Unused * Output: * None * Returns: * None * Called by: * IBTL framework for asynchronous events. * Description: * No special event handling currently. */ /* ARGSUSED */ static void umad_async_handler( void *private, ibt_hca_hdl_t hca_hdl, ibt_async_code_t code, ibt_async_event_t *event) { } /* * Need this ioctl to enable the newer interface (pkey_index and some * reserved key). Since OFED changed the abi without changing the abi * version. This resulted in wo abi interfaces (with and without the * pkey_index and some reserved bytes, but one abi version number. The * application then tries to do an ioctl() to enable the "newwer" interface * and it that ioctl succeeds, the application code assumes the newer abi * interface otherwise it assumes the older abi intrface (Uggggggg). */ static int umad_pkey_enable() { /* When we move to later releases of OFED, this will go away */ return (DDI_SUCCESS); } /* * Function: * umad_ioctl * Input: * dev device * cmd IB_USER_MAD_ENABLE_PKEY, IB_USER_MAD_REGISTER_AGENT or * IB_USER_MAD_UNREGISTER_AGENT * arg which agent to register or unregister * mode passed on to ddi_copyin() * credp Unused * rvalp Unused * Output: * None * Returns: * Error status * Called by: * Device ioctl framework * Description: * IB_USER_MAD_ENABLE_PKEY just allows the ioctl to succed to * indicate that we are at ABI version 5+, not really 5. * IB_USER_MAD_REGISTER_AGENT requests that a specific MAD class * for this device be handled by this process. * IB_USER_MAD_UNREGISTER_AGENT undoes the request above. */ /* ARGSUSED3 */ static int umad_ioctl( dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp) { int rc = 0; int minor; umad_info_t *info; umad_port_info_t *port; umad_uctx_t *uctx; struct ib_user_mad_reg_req req = {0}; info = ddi_get_soft_state(umad_statep, UMAD_INSTANCE); if (info == NULL) { rc = ENXIO; goto err1; } /* lookup the node and port #s */ minor = getminor(dev); if (ISSM_MINOR(minor)) { rc = ENXIO; goto err1; } mutex_enter(&info->info_mutex); uctx = info->info_uctx[GET_UCTX(minor)]; mutex_exit(&info->info_mutex); ASSERT(uctx != NULL); port = uctx->uctx_port; ASSERT(port != NULL); if (cmd == IB_USER_MAD_ENABLE_PKEY) return (umad_pkey_enable()); if (ddi_copyin((void *) arg, &req, sizeof (req), mode) != 0) { rc = EFAULT; goto err1; } switch (cmd) { case IB_USER_MAD_REGISTER_AGENT: mutex_enter(&uctx->uctx_lock); rc = umad_register(&req, uctx); mutex_exit(&uctx->uctx_lock); if (rc) goto err1; /* return agent ID to user */ rc = ddi_copyout(&req, (void *) arg, sizeof (req), mode); if (rc) { mutex_enter(&uctx->uctx_lock); (void) umad_unregister(&req, uctx); mutex_exit(&uctx->uctx_lock); rc = EFAULT; goto err1; } break; case IB_USER_MAD_UNREGISTER_AGENT: mutex_enter(&uctx->uctx_lock); rc = umad_unregister(&req, uctx); mutex_exit(&uctx->uctx_lock); break; default: rc = DDI_FAILURE; } err1: return (rc); } /* * Get a new unique agent ID. The agent list is already locked. The * complexity is not ideal, but the number of agents should be small * (ie 2 or 3) so it shouldn't matter. */ static int umad_get_new_agent_id(umad_uctx_t *uctx) { boolean_t found; unsigned int agent_id; llist_head_t *entry; agent_id = 0; ASSERT(MUTEX_HELD(&uctx->uctx_lock)); for (;;) { found = B_FALSE; list_for_each(entry, &uctx->uctx_agent_list) { umad_agent_t *agent = entry->ptr; if (agent_id == agent->agent_req.id) { found = B_TRUE; break; } } if (! found) break; agent_id++; } return (agent_id); } /* * Function: * umad_register * Input: * req User registration request * uctx User context * Output: * None * Returns: * status * Called by: * umad_ioctl * Description: * Handles the registration of user agents from userspace. * Each call will result in the creation of a new agent object for * the given HCA/port. If UMAD_CA_MAX_AGENTS has been reached then an * error is raised. */ static int umad_register(struct ib_user_mad_reg_req *req, umad_uctx_t *uctx) { int rc = IBMF_SUCCESS; umad_agent_t *agent = NULL; umad_port_info_t *port; /* check for valid QP */ if ((req->qpn != 0) && (req->qpn != 1)) { rc = EINVAL; goto err1; } ASSERT(MUTEX_HELD(&uctx->uctx_lock)); port = uctx->uctx_port; ASSERT(port != NULL); agent = umad_get_agent_by_class(uctx, req->mgmt_class); if (agent != NULL) return (IBMF_PORT_IN_USE); agent = kmem_zalloc(sizeof (umad_agent_t), KM_SLEEP); mutex_init(&agent->agent_lock, NULL, MUTEX_DRIVER, NULL); cv_init(&agent->agent_cv, NULL, CV_DRIVER, NULL); agent->agent_req = *req; agent->agent_uctx = uctx; llist_head_init(&agent->agent_list, agent); agent->agent_req.id = req->id = umad_get_new_agent_id(uctx); rc = umad_register_agent(agent); if (rc) goto err1; llist_add(&agent->agent_list, &uctx->uctx_agent_list); return (0); err1: if (rc) { if (agent) { cv_destroy(&agent->agent_cv); mutex_destroy(&agent->agent_lock); kmem_free(agent, sizeof (umad_agent_t)); } } return (rc); } /* * Function: * umad_unregister * Input: * req - user unregister request * info - user context * Output: * None * Returns: * Status * Called by: * umad_ioct * Description: * Undoes registration. Waits for pending operations before completing. */ static int umad_unregister(struct ib_user_mad_reg_req *req, umad_uctx_t *uctx) { int agent_id = req->id; umad_agent_t *agent; int rc; genlist_entry_t *entry; struct ibmf_reg_info *ibmf_info; boolean_t did_ibmf_unregister; umad_port_info_t *port; ASSERT(MUTEX_HELD(&uctx->uctx_lock)); agent = umad_get_agent_by_id(uctx, agent_id); if (agent == NULL) { rc = EINVAL; goto done; } mutex_enter(&agent->agent_lock); while (agent->agent_outstanding_msgs != 0) { agent->agent_flags |= UMAD_AGENT_UNREGISTERING; cv_wait(&agent->agent_cv, &agent->agent_lock); } if (agent->agent_flags & UMAD_HANDLING_ASYNC) agent->agent_reg->ibmf_reg_uctx = NULL; mutex_exit(&agent->agent_lock); /* Remove agent from the uctx list. */ llist_del(&agent->agent_list); /* Get the IBMF registration information */ ibmf_info = agent->agent_reg; mutex_enter(&ibmf_info->ibmf_reg_lock); /* Remove the pending received MADs. */ mutex_enter(&uctx->uctx_recv_lock); while ((entry = remove_genlist_head(&uctx->uctx_recv_list))) { ib_umad_msg_t *msg; ibmf_msg_t *ibmf_msg; mutex_exit(&uctx->uctx_recv_lock); msg = (ib_umad_msg_t *)entry->data; ibmf_msg = msg->umad_msg_ibmf_msg; rc = ibmf_free_msg(ibmf_info->ibmf_reg_handle, &ibmf_msg); ASSERT(rc == IBMF_SUCCESS); kmem_free(msg, sizeof (*msg)); mutex_enter(&uctx->uctx_recv_lock); } mutex_exit(&uctx->uctx_recv_lock); /* If no more references, tear down the ibmf registration */ if (--ibmf_info->ibmf_reg_refcnt == 0) { ibmf_info->ibmf_flags |= UMAD_IBMF_UNREGISTERING; mutex_exit(&ibmf_info->ibmf_reg_lock); /* Remove the callback */ rc = ibmf_tear_down_async_cb(ibmf_info->ibmf_reg_handle, IBMF_QP_HANDLE_DEFAULT, 0); #if defined(DEBUG) if (rc) { SOL_OFS_DPRINTF_L5(sol_umad_dbg_str, "umad_unregister: failed " "ibmf_tear_down_async_cb() error %d\n", rc); } #endif /* Remove the pending received MADs. */ mutex_enter(&uctx->uctx_recv_lock); while ((entry = remove_genlist_head(&uctx->uctx_recv_list))) { ib_umad_msg_t *msg; ibmf_msg_t *ibmf_msg; mutex_exit(&uctx->uctx_recv_lock); msg = (ib_umad_msg_t *)entry->data; ibmf_msg = msg->umad_msg_ibmf_msg; rc = ibmf_free_msg(ibmf_info->ibmf_reg_handle, &ibmf_msg); ASSERT(rc == IBMF_SUCCESS); kmem_free(msg, sizeof (*msg)); mutex_enter(&uctx->uctx_recv_lock); } mutex_exit(&uctx->uctx_recv_lock); /* unregister from IBMF */ rc = ibmf_unregister(&ibmf_info->ibmf_reg_handle, 0); #if defined(DEBUG) if (rc) { SOL_OFS_DPRINTF_L5(sol_umad_dbg_str, "umad_unregister: failed " "ibmf_unregister() error %d\n", rc); } #endif mutex_enter(&ibmf_info->ibmf_reg_lock); ibmf_info->ibmf_flags &= ~UMAD_IBMF_UNREGISTERING; cv_signal(&ibmf_info->ibmf_cv); mutex_exit(&ibmf_info->ibmf_reg_lock); did_ibmf_unregister = B_TRUE; } else { mutex_exit(&ibmf_info->ibmf_reg_lock); did_ibmf_unregister = B_FALSE; } if (did_ibmf_unregister) { llist_head_t *entry; struct ibmf_reg_info *ibmf_entry = NULL; #if defined(DEBUG) boolean_t found = B_FALSE; #endif port = uctx->uctx_port; mutex_enter(&port->port_lock); list_for_each(entry, &port->port_ibmf_regs) { ibmf_entry = entry->ptr; if (ibmf_info == ibmf_entry) { #if defined(DEBUG) found = B_TRUE; #endif break; } } ASSERT(found); llist_del(entry); kmem_free(entry, sizeof (*entry)); mutex_exit(&port->port_lock); /* Release the registration memory */ kmem_free(ibmf_info, sizeof (*ibmf_info)); } agent->agent_uctx = NULL; cv_destroy(&agent->agent_cv); mutex_destroy(&agent->agent_lock); kmem_free(agent, sizeof (*agent)); rc = 0; done: return (rc); } /* * Function: * umad_poll * Input: * dev device * events which events * anyyet any events yet? * Output: * reventsp return of which events * phpp poll head pointer * Returns: * return 0 for success, or the appropriate error number * Called by: * Device poll framework * Description: * Fails for ISSM device. POLLOUT is always true. POLLIN or POLLRDNORM * is true if a message has been queued for the user context receive list. */ static int umad_poll( dev_t dev, short events, int anyyet, short *reventsp, struct pollhead **phpp) { int rc = 0; int minor; umad_uctx_t *uctx; umad_port_info_t *port; umad_info_t *info; short revent = 0; info = ddi_get_soft_state(umad_statep, UMAD_INSTANCE); if (info == NULL) { rc = ENXIO; goto err1; } /* lookup the node and port #s */ minor = getminor(dev); if (ISSM_MINOR(minor)) { rc = ENXIO; goto err1; } mutex_enter(&info->info_mutex); uctx = info->info_uctx[GET_UCTX(minor)]; mutex_exit(&info->info_mutex); ASSERT(uctx != NULL); port = uctx->uctx_port; ASSERT(port != NULL); /* * Always signal ready for POLLOUT / POLLWRNORM. * Signal for POLLIN / POLLRDNORM whenever there is something in * the receive list. */ if (events & POLLOUT) { revent = POLLOUT; } else if (events & (POLLIN | POLLRDNORM)) { mutex_enter(&uctx->uctx_recv_lock); if (! genlist_empty(&uctx->uctx_recv_list)) { revent |= POLLIN | POLLRDNORM; } mutex_exit(&uctx->uctx_recv_lock); } if (revent == 0) { if (! anyyet) *phpp = &uctx->uctx_pollhead; } *reventsp = revent; err1: return (rc); } /* * Function: * umad_unsolicited_cb * Input: * ibmf_handle - handle to ibmf * msgp - The incoming SM MAD * args - umad_port_info_t object that the MAD came in on * Output: * None * Returns: * none * Called by: * IBMF from below * Description: * Callback function (ibmf_msg_cb_t) that is invoked when the * ibmf receives a response MAD and passes it up if requested. * The message is tossed if no one wants it or queued if requested. */ static void umad_unsolicited_cb(ibmf_handle_t ibmf_handle, ibmf_msg_t *msgp, void *args) { struct ibmf_reg_info *ibmf_info = (struct ibmf_reg_info *)args; struct umad_agent_s *agent; ib_mad_hdr_t *mad_hdr; int rc; #if defined(__lint) ibmf_handle = 0; #endif ASSERT(msgp->im_msgbufs_send.im_bufs_mad_hdr == NULL); ASSERT(msgp->im_msgbufs_send.im_bufs_cl_data == NULL); ASSERT(msgp->im_msgbufs_send.im_bufs_cl_data_len == 0); /* Apply the filters to this MAD. */ mad_hdr = msgp->im_msgbufs_recv.im_bufs_mad_hdr; mutex_enter(&ibmf_info->ibmf_reg_lock); /* * Make sure the user context that was receiving the unsolicited * messages is still present. */ if (ibmf_info->ibmf_reg_uctx == NULL) goto reject; mutex_enter(&ibmf_info->ibmf_reg_uctx->uctx_lock); agent = umad_get_agent_by_class(ibmf_info->ibmf_reg_uctx, mad_hdr->MgmtClass); mutex_exit(&ibmf_info->ibmf_reg_uctx->uctx_lock); if (agent == NULL) goto reject; if (mad_hdr->ClassVersion != agent->agent_req.mgmt_class_version) goto reject; if (! is_supported_mad_method(mad_hdr->R_Method & MAD_METHOD_MASK, agent->agent_req.method_mask)) goto reject; if (umad_queue_mad_msg(agent, msgp)) goto reject; mutex_exit(&ibmf_info->ibmf_reg_lock); return; reject: rc = ibmf_free_msg(ibmf_info->ibmf_reg_handle, &msgp); ASSERT(rc == IBMF_SUCCESS); mutex_exit(&ibmf_info->ibmf_reg_lock); } #if defined(__lint) /* * This is needed because rdma/ib_verbs.h and sol_ofs/sol_ofs_common.h * both implement static functions. Not all of those functions are * used by sol_umad, but lint doesn't like seeing static function that * are defined but not used. */ void lint_function(llist_head_t *a, llist_head_t *b) { (void) llist_is_last(a, b); llist_add_tail(a, b); (void) ib_width_enum_to_int(IB_WIDTH_1X); } #endif