/* * Copyright (c) 2004, 2005 Intel Corporation. All rights reserved. * Copyright (c) 2005, 2006 Voltaire Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU * General Public License (GPL) Version 2, available from the file * COPYING in the main directorY of this source tree, or the * OpenIB.org BSD license below: * * Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * - Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. * * - Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * $Id$ */ #include #include #include #include #include #include MODULE_AUTHOR("Sean Hefty"); MODULE_DESCRIPTION("InfiniBand MAD viewer"); MODULE_LICENSE("Dual BSD/GPL"); static void madeye_remove_one(struct ib_device *device); static void madeye_add_one(struct ib_device *device); static struct ib_client madeye_client = { .name = "madeye", .add = madeye_add_one, .remove = madeye_remove_one }; struct madeye_port { struct ib_mad_agent *smi_agent; struct ib_mad_agent *gsi_agent; }; static int smp = 1; static int gmp = 1; static int mgmt_class = 0; static int attr_id = 0; static int data = 0; module_param(smp, int, 0444); module_param(gmp, int, 0444); module_param(mgmt_class, int, 0444); module_param(attr_id, int, 0444); module_param(data, int, 0444); MODULE_PARM_DESC(smp, "Display all SMPs (default=1)"); MODULE_PARM_DESC(gmp, "Display all GMPs (default=1)"); MODULE_PARM_DESC(mgmt_class, "Display all MADs of specified class (default=0)"); MODULE_PARM_DESC(attr_id, "Display add MADs of specified attribute ID (default=0)"); MODULE_PARM_DESC(data, "Display data area of MADs (default=0)"); static char * get_class_name(u8 mgmt_class) { switch(mgmt_class) { case IB_MGMT_CLASS_SUBN_LID_ROUTED: return "LID routed SMP"; case IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE: return "Directed route SMP"; case IB_MGMT_CLASS_SUBN_ADM: return "Subnet admin."; case IB_MGMT_CLASS_PERF_MGMT: return "Perf. mgmt."; case IB_MGMT_CLASS_BM: return "Baseboard mgmt."; case IB_MGMT_CLASS_DEVICE_MGMT: return "Device mgmt."; case IB_MGMT_CLASS_CM: return "Comm. mgmt."; case IB_MGMT_CLASS_SNMP: return "SNMP"; default: return "Unknown vendor/application"; } } static char * get_method_name(u8 mgmt_class, u8 method) { switch(method) { case IB_MGMT_METHOD_GET: return "Get"; case IB_MGMT_METHOD_SET: return "Set"; case IB_MGMT_METHOD_GET_RESP: return "Get response"; case IB_MGMT_METHOD_SEND: return "Send"; case IB_MGMT_METHOD_SEND | IB_MGMT_METHOD_RESP: return "Send response"; case IB_MGMT_METHOD_TRAP: return "Trap"; case IB_MGMT_METHOD_REPORT: return "Report"; case IB_MGMT_METHOD_REPORT_RESP: return "Report response"; case IB_MGMT_METHOD_TRAP_REPRESS: return "Trap repress"; default: break; } switch (mgmt_class) { case IB_MGMT_CLASS_SUBN_ADM: switch (method) { case IB_SA_METHOD_GET_TABLE: return "Get table"; case IB_SA_METHOD_GET_TABLE_RESP: return "Get table response"; case IB_SA_METHOD_DELETE: return "Delete"; case IB_SA_METHOD_DELETE_RESP: return "Delete response"; case IB_SA_METHOD_GET_MULTI: return "Get Multi"; case IB_SA_METHOD_GET_MULTI_RESP: return "Get Multi response"; case IB_SA_METHOD_GET_TRACE_TBL: return "Get Trace Table response"; default: break; } default: break; } return "Unknown"; } static void print_status_details(u16 status) { if (status & 0x0001) printk(" busy\n"); if (status & 0x0002) printk(" redirection required\n"); switch((status & 0x001C) >> 2) { case 1: printk(" bad version\n"); break; case 2: printk(" method not supported\n"); break; case 3: printk(" method/attribute combo not supported\n"); break; case 7: printk(" invalid attribute/modifier value\n"); break; } } static char * get_sa_attr(__be16 attr) { switch(attr) { case IB_SA_ATTR_CLASS_PORTINFO: return "Class Port Info"; case IB_SA_ATTR_NOTICE: return "Notice"; case IB_SA_ATTR_INFORM_INFO: return "Inform Info"; case IB_SA_ATTR_NODE_REC: return "Node Record"; case IB_SA_ATTR_PORT_INFO_REC: return "PortInfo Record"; case IB_SA_ATTR_SL2VL_REC: return "SL to VL Record"; case IB_SA_ATTR_SWITCH_REC: return "Switch Record"; case IB_SA_ATTR_LINEAR_FDB_REC: return "Linear FDB Record"; case IB_SA_ATTR_RANDOM_FDB_REC: return "Random FDB Record"; case IB_SA_ATTR_MCAST_FDB_REC: return "Multicast FDB Record"; case IB_SA_ATTR_SM_INFO_REC: return "SM Info Record"; case IB_SA_ATTR_LINK_REC: return "Link Record"; case IB_SA_ATTR_GUID_INFO_REC: return "Guid Info Record"; case IB_SA_ATTR_SERVICE_REC: return "Service Record"; case IB_SA_ATTR_PARTITION_REC: return "Partition Record"; case IB_SA_ATTR_PATH_REC: return "Path Record"; case IB_SA_ATTR_VL_ARB_REC: return "VL Arb Record"; case IB_SA_ATTR_MC_MEMBER_REC: return "MC Member Record"; case IB_SA_ATTR_TRACE_REC: return "Trace Record"; case IB_SA_ATTR_MULTI_PATH_REC: return "Multi Path Record"; case IB_SA_ATTR_SERVICE_ASSOC_REC: return "Service Assoc Record"; case IB_SA_ATTR_INFORM_INFO_REC: return "Inform Info Record"; default: return ""; } } static void print_mad_hdr(struct ib_mad_hdr *mad_hdr) { printk("MAD version....0x%01x\n", mad_hdr->base_version); printk("Class..........0x%01x (%s)\n", mad_hdr->mgmt_class, get_class_name(mad_hdr->mgmt_class)); printk("Class version..0x%01x\n", mad_hdr->class_version); printk("Method.........0x%01x (%s)\n", mad_hdr->method, get_method_name(mad_hdr->mgmt_class, mad_hdr->method)); printk("Status.........0x%02x\n", be16_to_cpu(mad_hdr->status)); if (mad_hdr->status) print_status_details(be16_to_cpu(mad_hdr->status)); printk("Class specific.0x%02x\n", be16_to_cpu(mad_hdr->class_specific)); printk("Trans ID.......0x%llx\n", (unsigned long long)be64_to_cpu(mad_hdr->tid)); if (mad_hdr->mgmt_class == IB_MGMT_CLASS_SUBN_ADM) printk("Attr ID........0x%02x (%s)\n", be16_to_cpu(mad_hdr->attr_id), get_sa_attr(be16_to_cpu(mad_hdr->attr_id))); else printk("Attr ID........0x%02x\n", be16_to_cpu(mad_hdr->attr_id)); printk("Attr modifier..0x%04x\n", be32_to_cpu(mad_hdr->attr_mod)); } static char * get_rmpp_type(u8 rmpp_type) { switch (rmpp_type) { case IB_MGMT_RMPP_TYPE_DATA: return "Data"; case IB_MGMT_RMPP_TYPE_ACK: return "Ack"; case IB_MGMT_RMPP_TYPE_STOP: return "Stop"; case IB_MGMT_RMPP_TYPE_ABORT: return "Abort"; default: return "Unknown"; } } static char * get_rmpp_flags(u8 rmpp_flags) { if (rmpp_flags & IB_MGMT_RMPP_FLAG_ACTIVE) if (rmpp_flags & IB_MGMT_RMPP_FLAG_FIRST) if (rmpp_flags & IB_MGMT_RMPP_FLAG_LAST) return "Active - First & Last"; else return "Active - First"; else if (rmpp_flags & IB_MGMT_RMPP_FLAG_LAST) return "Active - Last"; else return "Active"; else return "Inactive"; } static void print_rmpp_hdr(struct ib_rmpp_hdr *rmpp_hdr) { printk("RMPP version...0x%01x\n", rmpp_hdr->rmpp_version); printk("RMPP type......0x%01x (%s)\n", rmpp_hdr->rmpp_type, get_rmpp_type(rmpp_hdr->rmpp_type)); printk("RMPP RRespTime.0x%01x\n", ib_get_rmpp_resptime(rmpp_hdr)); printk("RMPP flags.....0x%01x (%s)\n", ib_get_rmpp_flags(rmpp_hdr), get_rmpp_flags(ib_get_rmpp_flags(rmpp_hdr))); printk("RMPP status....0x%01x\n", rmpp_hdr->rmpp_status); printk("Seg number.....0x%04x\n", be32_to_cpu(rmpp_hdr->seg_num)); switch (rmpp_hdr->rmpp_type) { case IB_MGMT_RMPP_TYPE_DATA: printk("Payload len....0x%04x\n", be32_to_cpu(rmpp_hdr->paylen_newwin)); break; case IB_MGMT_RMPP_TYPE_ACK: printk("New window.....0x%04x\n", be32_to_cpu(rmpp_hdr->paylen_newwin)); break; default: printk("Data 2.........0x%04x\n", be32_to_cpu(rmpp_hdr->paylen_newwin)); break; } } static char * get_smp_attr(__be16 attr) { switch (attr) { case IB_SMP_ATTR_NOTICE: return "notice"; case IB_SMP_ATTR_NODE_DESC: return "node description"; case IB_SMP_ATTR_NODE_INFO: return "node info"; case IB_SMP_ATTR_SWITCH_INFO: return "switch info"; case IB_SMP_ATTR_GUID_INFO: return "GUID info"; case IB_SMP_ATTR_PORT_INFO: return "port info"; case IB_SMP_ATTR_PKEY_TABLE: return "pkey table"; case IB_SMP_ATTR_SL_TO_VL_TABLE: return "SL to VL table"; case IB_SMP_ATTR_VL_ARB_TABLE: return "VL arbitration table"; case IB_SMP_ATTR_LINEAR_FORWARD_TABLE: return "linear forwarding table"; case IB_SMP_ATTR_RANDOM_FORWARD_TABLE: return "random forward table"; case IB_SMP_ATTR_MCAST_FORWARD_TABLE: return "multicast forward table"; case IB_SMP_ATTR_SM_INFO: return "SM info"; case IB_SMP_ATTR_VENDOR_DIAG: return "vendor diags"; case IB_SMP_ATTR_LED_INFO: return "LED info"; default: return ""; } } static void print_smp(struct ib_smp *smp) { int i; printk("MAD version....0x%01x\n", smp->base_version); printk("Class..........0x%01x (%s)\n", smp->mgmt_class, get_class_name(smp->mgmt_class)); printk("Class version..0x%01x\n", smp->class_version); printk("Method.........0x%01x (%s)\n", smp->method, get_method_name(smp->mgmt_class, smp->method)); printk("Status.........0x%02x\n", be16_to_cpu(smp->status)); if (smp->status) print_status_details(be16_to_cpu(smp->status)); printk("Hop pointer....0x%01x\n", smp->hop_ptr); printk("Hop counter....0x%01x\n", smp->hop_cnt); printk("Trans ID.......0x%llx\n", (unsigned long long)be64_to_cpu(smp->tid)); printk("Attr ID........0x%02x (%s)\n", be16_to_cpu(smp->attr_id), get_smp_attr(smp->attr_id)); printk("Attr modifier..0x%04x\n", be32_to_cpu(smp->attr_mod)); printk("Mkey...........0x%llx\n", (unsigned long long)be64_to_cpu(smp->mkey)); printk("DR SLID........0x%02x\n", be16_to_cpu(smp->dr_slid)); printk("DR DLID........0x%02x", be16_to_cpu(smp->dr_dlid)); if (data) { for (i = 0; i < IB_SMP_DATA_SIZE; i++) { if (i % 16 == 0) printk("\nSMP Data......."); printk("%01x ", smp->data[i]); } for (i = 0; i < IB_SMP_MAX_PATH_HOPS; i++) { if (i % 16 == 0) printk("\nInitial path..."); printk("%01x ", smp->initial_path[i]); } for (i = 0; i < IB_SMP_MAX_PATH_HOPS; i++) { if (i % 16 == 0) printk("\nReturn path...."); printk("%01x ", smp->return_path[i]); } } printk("\n"); } static void snoop_smi_handler(struct ib_mad_agent *mad_agent, struct ib_mad_send_buf *send_buf, struct ib_mad_send_wc *mad_send_wc) { struct ib_mad_hdr *hdr = send_buf->mad; if (!smp && hdr->mgmt_class != mgmt_class) return; if (attr_id && be16_to_cpu(hdr->attr_id) != attr_id) return; printk("Madeye:sent SMP\n"); print_smp(send_buf->mad); } static void recv_smi_handler(struct ib_mad_agent *mad_agent, struct ib_mad_recv_wc *mad_recv_wc) { if (!smp && mad_recv_wc->recv_buf.mad->mad_hdr.mgmt_class != mgmt_class) return; if (attr_id && be16_to_cpu(mad_recv_wc->recv_buf.mad->mad_hdr.attr_id) != attr_id) return; printk("Madeye:recv SMP\n"); print_smp((struct ib_smp *)&mad_recv_wc->recv_buf.mad->mad_hdr); } static int is_rmpp_mad(struct ib_mad_hdr *mad_hdr) { if (mad_hdr->mgmt_class == IB_MGMT_CLASS_SUBN_ADM) { switch (mad_hdr->method) { case IB_SA_METHOD_GET_TABLE: case IB_SA_METHOD_GET_TABLE_RESP: case IB_SA_METHOD_GET_MULTI_RESP: return 1; default: break; } } else if ((mad_hdr->mgmt_class >= IB_MGMT_CLASS_VENDOR_RANGE2_START) && (mad_hdr->mgmt_class <= IB_MGMT_CLASS_VENDOR_RANGE2_END)) return 1; return 0; } static void snoop_gsi_handler(struct ib_mad_agent *mad_agent, struct ib_mad_send_buf *send_buf, struct ib_mad_send_wc *mad_send_wc) { struct ib_mad_hdr *hdr = send_buf->mad; if (!gmp && hdr->mgmt_class != mgmt_class) return; if (attr_id && be16_to_cpu(hdr->attr_id) != attr_id) return; printk("Madeye:sent GMP\n"); print_mad_hdr(hdr); if (is_rmpp_mad(hdr)) print_rmpp_hdr(&((struct ib_rmpp_mad *) hdr)->rmpp_hdr); } static void recv_gsi_handler(struct ib_mad_agent *mad_agent, struct ib_mad_recv_wc *mad_recv_wc) { struct ib_mad_hdr *hdr = &mad_recv_wc->recv_buf.mad->mad_hdr; struct ib_rmpp_mad *mad = NULL; struct ib_sa_mad *sa_mad; struct ib_vendor_mad *vendor_mad; u8 *mad_data; int i, j; if (!gmp && hdr->mgmt_class != mgmt_class) return; if (attr_id && be16_to_cpu(mad_recv_wc->recv_buf.mad->mad_hdr.attr_id) != attr_id) return; printk("Madeye:recv GMP\n"); print_mad_hdr(hdr); if (is_rmpp_mad(hdr)) { mad = (struct ib_rmpp_mad *) hdr; print_rmpp_hdr(&mad->rmpp_hdr); } if (data) { if (hdr->mgmt_class == IB_MGMT_CLASS_SUBN_ADM) { j = IB_MGMT_SA_DATA; /* Display SA header */ if (is_rmpp_mad(hdr) && mad->rmpp_hdr.rmpp_type != IB_MGMT_RMPP_TYPE_DATA) return; sa_mad = (struct ib_sa_mad *) &mad_recv_wc->recv_buf.mad; mad_data = sa_mad->data; } else { if (is_rmpp_mad(hdr)) { j = IB_MGMT_VENDOR_DATA; /* Display OUI */ vendor_mad = (struct ib_vendor_mad *) &mad_recv_wc->recv_buf.mad; printk("Vendor OUI......%01x %01x %01x\n", vendor_mad->oui[0], vendor_mad->oui[1], vendor_mad->oui[2]); mad_data = vendor_mad->data; } else { j = IB_MGMT_MAD_DATA; mad_data = mad_recv_wc->recv_buf.mad->data; } } for (i = 0; i < j; i++) { if (i % 16 == 0) printk("\nData..........."); printk("%01x ", mad_data[i]); } printk("\n"); } } static void madeye_add_one(struct ib_device *device) { struct madeye_port *port; int reg_flags; u8 i, s, e; if (device->node_type == RDMA_NODE_IB_SWITCH) { s = 0; e = 0; } else { s = 1; e = device->phys_port_cnt; } port = kmalloc(sizeof *port * (e - s + 1), GFP_KERNEL); if (!port) goto out; reg_flags = IB_MAD_SNOOP_SEND_COMPLETIONS | IB_MAD_SNOOP_RECVS; for (i = 0; i <= e - s; i++) { port[i].smi_agent = ib_register_mad_snoop(device, i + s, IB_QPT_SMI, reg_flags, snoop_smi_handler, recv_smi_handler, &port[i]); port[i].gsi_agent = ib_register_mad_snoop(device, i + s, IB_QPT_GSI, reg_flags, snoop_gsi_handler, recv_gsi_handler, &port[i]); } out: ib_set_client_data(device, &madeye_client, port); } static void madeye_remove_one(struct ib_device *device) { struct madeye_port *port; int i, s, e; port = (struct madeye_port *) ib_get_client_data(device, &madeye_client); if (!port) return; if (device->node_type == RDMA_NODE_IB_SWITCH) { s = 0; e = 0; } else { s = 1; e = device->phys_port_cnt; } for (i = 0; i <= e - s; i++) { if (!IS_ERR(port[i].smi_agent)) ib_unregister_mad_agent(port[i].smi_agent); if (!IS_ERR(port[i].gsi_agent)) ib_unregister_mad_agent(port[i].gsi_agent); } kfree(port); } static int __init ib_madeye_init(void) { return ib_register_client(&madeye_client); } static void __exit ib_madeye_cleanup(void) { ib_unregister_client(&madeye_client); } module_init(ib_madeye_init); module_exit(ib_madeye_cleanup);