1/*
2 * Copyright (c) 2004, 2005 Intel Corporation.  All rights reserved.
3 * Copyright (c) 2005, 2006 Voltaire Inc.  All rights reserved.
4 *
5 * This software is available to you under a choice of one of two
6 * licenses.  You may choose to be licensed under the terms of the GNU
7 * General Public License (GPL) Version 2, available from the file
8 * COPYING in the main directorY of this source tree, or the
9 * OpenIB.org BSD license below:
10 *
11 *     Redistribution and use in source and binary forms, with or
12 *     without modification, are permitted provided that the following
13 *     conditions are met:
14 *
15 *      - Redistributions of source code must retain the above
16 *        copyright notice, this list of conditions and the following
17 *        disclaimer.
18 *
19 *      - Redistributions in binary form must reproduce the above
20 *        copyright notice, this list of conditions and the following
21 *        disclaimer in the documentation and/or other materials
22 *        provided with the distribution.
23 *
24 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
28 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
29 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
30 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
31 * SOFTWARE.
32 *
33 * $Id$
34 */
35#include <linux/module.h>
36#include <linux/device.h>
37#include <linux/err.h>
38
39#include <rdma/ib_mad.h>
40#include <rdma/ib_smi.h>
41#include <rdma/ib_sa.h>
42
43MODULE_AUTHOR("Sean Hefty");
44MODULE_DESCRIPTION("InfiniBand MAD viewer");
45MODULE_LICENSE("Dual BSD/GPL");
46
47static void madeye_remove_one(struct ib_device *device);
48static void madeye_add_one(struct ib_device *device);
49
50static struct ib_client madeye_client = {
51	.name   = "madeye",
52	.add    = madeye_add_one,
53	.remove = madeye_remove_one
54};
55
56struct madeye_port {
57	struct ib_mad_agent *smi_agent;
58	struct ib_mad_agent *gsi_agent;
59};
60
61static int smp = 1;
62static int gmp = 1;
63static int mgmt_class = 0;
64static int attr_id = 0;
65static int data = 0;
66
67module_param(smp, int, 0444);
68module_param(gmp, int, 0444);
69module_param(mgmt_class, int, 0444);
70module_param(attr_id, int, 0444);
71module_param(data, int, 0444);
72
73MODULE_PARM_DESC(smp, "Display all SMPs (default=1)");
74MODULE_PARM_DESC(gmp, "Display all GMPs (default=1)");
75MODULE_PARM_DESC(mgmt_class, "Display all MADs of specified class (default=0)");
76MODULE_PARM_DESC(attr_id, "Display add MADs of specified attribute ID (default=0)");
77MODULE_PARM_DESC(data, "Display data area of MADs (default=0)");
78
79static char * get_class_name(u8 mgmt_class)
80{
81	switch(mgmt_class) {
82	case IB_MGMT_CLASS_SUBN_LID_ROUTED:
83		return "LID routed SMP";
84	case IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE:
85		return "Directed route SMP";
86	case IB_MGMT_CLASS_SUBN_ADM:
87		return "Subnet admin.";
88	case IB_MGMT_CLASS_PERF_MGMT:
89		return "Perf. mgmt.";
90	case IB_MGMT_CLASS_BM:
91		return "Baseboard mgmt.";
92	case IB_MGMT_CLASS_DEVICE_MGMT:
93		return "Device mgmt.";
94	case IB_MGMT_CLASS_CM:
95		return "Comm. mgmt.";
96	case IB_MGMT_CLASS_SNMP:
97		return "SNMP";
98	default:
99		return "Unknown vendor/application";
100	}
101}
102
103static char * get_method_name(u8 mgmt_class, u8 method)
104{
105	switch(method) {
106	case IB_MGMT_METHOD_GET:
107		return "Get";
108	case IB_MGMT_METHOD_SET:
109		return "Set";
110	case IB_MGMT_METHOD_GET_RESP:
111		return "Get response";
112	case IB_MGMT_METHOD_SEND:
113		return "Send";
114	case IB_MGMT_METHOD_SEND | IB_MGMT_METHOD_RESP:
115		return "Send response";
116	case IB_MGMT_METHOD_TRAP:
117		return "Trap";
118	case IB_MGMT_METHOD_REPORT:
119		return "Report";
120	case IB_MGMT_METHOD_REPORT_RESP:
121		return "Report response";
122	case IB_MGMT_METHOD_TRAP_REPRESS:
123		return "Trap repress";
124	default:
125		break;
126	}
127
128	switch (mgmt_class) {
129	case IB_MGMT_CLASS_SUBN_ADM:
130		switch (method) {
131		case IB_SA_METHOD_GET_TABLE:
132			return "Get table";
133		case IB_SA_METHOD_GET_TABLE_RESP:
134			return "Get table response";
135		case IB_SA_METHOD_DELETE:
136			return "Delete";
137		case IB_SA_METHOD_DELETE_RESP:
138			return "Delete response";
139		case IB_SA_METHOD_GET_MULTI:
140			return "Get Multi";
141		case IB_SA_METHOD_GET_MULTI_RESP:
142			return "Get Multi response";
143		case IB_SA_METHOD_GET_TRACE_TBL:
144			return "Get Trace Table response";
145		default:
146			break;
147		}
148	default:
149		break;
150	}
151
152	return "Unknown";
153}
154
155static void print_status_details(u16 status)
156{
157	if (status & 0x0001)
158		printk("               busy\n");
159	if (status & 0x0002)
160		printk("               redirection required\n");
161	switch((status & 0x001C) >> 2) {
162	case 1:
163		printk("               bad version\n");
164		break;
165	case 2:
166		printk("               method not supported\n");
167		break;
168	case 3:
169		printk("               method/attribute combo not supported\n");
170		break;
171	case 7:
172		printk("               invalid attribute/modifier value\n");
173		break;
174	}
175}
176
177static char * get_sa_attr(__be16 attr)
178{
179	switch(attr) {
180	case IB_SA_ATTR_CLASS_PORTINFO:
181		return "Class Port Info";
182	case IB_SA_ATTR_NOTICE:
183		return "Notice";
184	case IB_SA_ATTR_INFORM_INFO:
185		return "Inform Info";
186	case IB_SA_ATTR_NODE_REC:
187		return "Node Record";
188	case IB_SA_ATTR_PORT_INFO_REC:
189		return "PortInfo Record";
190	case IB_SA_ATTR_SL2VL_REC:
191		return "SL to VL Record";
192	case IB_SA_ATTR_SWITCH_REC:
193		return "Switch Record";
194	case IB_SA_ATTR_LINEAR_FDB_REC:
195		return "Linear FDB Record";
196	case IB_SA_ATTR_RANDOM_FDB_REC:
197		return "Random FDB Record";
198	case IB_SA_ATTR_MCAST_FDB_REC:
199		return "Multicast FDB Record";
200	case IB_SA_ATTR_SM_INFO_REC:
201		return "SM Info Record";
202	case IB_SA_ATTR_LINK_REC:
203		return "Link Record";
204	case IB_SA_ATTR_GUID_INFO_REC:
205		return "Guid Info Record";
206	case IB_SA_ATTR_SERVICE_REC:
207		return "Service Record";
208	case IB_SA_ATTR_PARTITION_REC:
209		return "Partition Record";
210	case IB_SA_ATTR_PATH_REC:
211		return "Path Record";
212	case IB_SA_ATTR_VL_ARB_REC:
213		return "VL Arb Record";
214	case IB_SA_ATTR_MC_MEMBER_REC:
215		return "MC Member Record";
216	case IB_SA_ATTR_TRACE_REC:
217		return "Trace Record";
218	case IB_SA_ATTR_MULTI_PATH_REC:
219		return "Multi Path Record";
220	case IB_SA_ATTR_SERVICE_ASSOC_REC:
221		return "Service Assoc Record";
222	case IB_SA_ATTR_INFORM_INFO_REC:
223		return "Inform Info Record";
224	default:
225		return "";
226	}
227}
228
229static void print_mad_hdr(struct ib_mad_hdr *mad_hdr)
230{
231	printk("MAD version....0x%01x\n", mad_hdr->base_version);
232	printk("Class..........0x%01x (%s)\n", mad_hdr->mgmt_class,
233	       get_class_name(mad_hdr->mgmt_class));
234	printk("Class version..0x%01x\n", mad_hdr->class_version);
235	printk("Method.........0x%01x (%s)\n", mad_hdr->method,
236	       get_method_name(mad_hdr->mgmt_class, mad_hdr->method));
237	printk("Status.........0x%02x\n", be16_to_cpu(mad_hdr->status));
238	if (mad_hdr->status)
239		print_status_details(be16_to_cpu(mad_hdr->status));
240	printk("Class specific.0x%02x\n", be16_to_cpu(mad_hdr->class_specific));
241	printk("Trans ID.......0x%llx\n",
242		(unsigned long long)be64_to_cpu(mad_hdr->tid));
243	if (mad_hdr->mgmt_class == IB_MGMT_CLASS_SUBN_ADM)
244		printk("Attr ID........0x%02x (%s)\n",
245		       be16_to_cpu(mad_hdr->attr_id),
246		       get_sa_attr(be16_to_cpu(mad_hdr->attr_id)));
247	else
248		printk("Attr ID........0x%02x\n",
249		       be16_to_cpu(mad_hdr->attr_id));
250	printk("Attr modifier..0x%04x\n", be32_to_cpu(mad_hdr->attr_mod));
251}
252
253static char * get_rmpp_type(u8 rmpp_type)
254{
255	switch (rmpp_type) {
256	case IB_MGMT_RMPP_TYPE_DATA:
257		return "Data";
258	case IB_MGMT_RMPP_TYPE_ACK:
259		return "Ack";
260	case IB_MGMT_RMPP_TYPE_STOP:
261		return "Stop";
262	case IB_MGMT_RMPP_TYPE_ABORT:
263		return "Abort";
264	default:
265		return "Unknown";
266	}
267}
268
269static char * get_rmpp_flags(u8 rmpp_flags)
270{
271	if (rmpp_flags & IB_MGMT_RMPP_FLAG_ACTIVE)
272		if (rmpp_flags & IB_MGMT_RMPP_FLAG_FIRST)
273			if (rmpp_flags & IB_MGMT_RMPP_FLAG_LAST)
274				return "Active - First & Last";
275			else
276				return "Active - First";
277		else
278			if (rmpp_flags & IB_MGMT_RMPP_FLAG_LAST)
279				return "Active - Last";
280			else
281				return "Active";
282	else
283		return "Inactive";
284}
285
286static void print_rmpp_hdr(struct ib_rmpp_hdr *rmpp_hdr)
287{
288	printk("RMPP version...0x%01x\n", rmpp_hdr->rmpp_version);
289	printk("RMPP type......0x%01x (%s)\n", rmpp_hdr->rmpp_type,
290	       get_rmpp_type(rmpp_hdr->rmpp_type));
291	printk("RMPP RRespTime.0x%01x\n", ib_get_rmpp_resptime(rmpp_hdr));
292	printk("RMPP flags.....0x%01x (%s)\n", ib_get_rmpp_flags(rmpp_hdr),
293	       get_rmpp_flags(ib_get_rmpp_flags(rmpp_hdr)));
294	printk("RMPP status....0x%01x\n", rmpp_hdr->rmpp_status);
295	printk("Seg number.....0x%04x\n", be32_to_cpu(rmpp_hdr->seg_num));
296	switch (rmpp_hdr->rmpp_type) {
297	case IB_MGMT_RMPP_TYPE_DATA:
298		printk("Payload len....0x%04x\n",
299		       be32_to_cpu(rmpp_hdr->paylen_newwin));
300		break;
301	case IB_MGMT_RMPP_TYPE_ACK:
302		printk("New window.....0x%04x\n",
303		       be32_to_cpu(rmpp_hdr->paylen_newwin));
304		break;
305	default:
306		printk("Data 2.........0x%04x\n",
307		       be32_to_cpu(rmpp_hdr->paylen_newwin));
308		break;
309	}
310}
311
312static char * get_smp_attr(__be16 attr)
313{
314	switch (attr) {
315	case IB_SMP_ATTR_NOTICE:
316		return "notice";
317	case IB_SMP_ATTR_NODE_DESC:
318		return "node description";
319	case IB_SMP_ATTR_NODE_INFO:
320		return "node info";
321	case IB_SMP_ATTR_SWITCH_INFO:
322		return "switch info";
323	case IB_SMP_ATTR_GUID_INFO:
324		return "GUID info";
325	case IB_SMP_ATTR_PORT_INFO:
326		return "port info";
327	case IB_SMP_ATTR_PKEY_TABLE:
328		return "pkey table";
329	case IB_SMP_ATTR_SL_TO_VL_TABLE:
330		return "SL to VL table";
331	case IB_SMP_ATTR_VL_ARB_TABLE:
332		return "VL arbitration table";
333	case IB_SMP_ATTR_LINEAR_FORWARD_TABLE:
334		return "linear forwarding table";
335	case IB_SMP_ATTR_RANDOM_FORWARD_TABLE:
336		return "random forward table";
337	case IB_SMP_ATTR_MCAST_FORWARD_TABLE:
338		return "multicast forward table";
339	case IB_SMP_ATTR_SM_INFO:
340		return "SM info";
341	case IB_SMP_ATTR_VENDOR_DIAG:
342		return "vendor diags";
343	case IB_SMP_ATTR_LED_INFO:
344		return "LED info";
345	default:
346		return "";
347	}
348}
349
350static void print_smp(struct ib_smp *smp)
351{
352	int i;
353
354	printk("MAD version....0x%01x\n", smp->base_version);
355	printk("Class..........0x%01x (%s)\n", smp->mgmt_class,
356	       get_class_name(smp->mgmt_class));
357	printk("Class version..0x%01x\n", smp->class_version);
358	printk("Method.........0x%01x (%s)\n", smp->method,
359	       get_method_name(smp->mgmt_class, smp->method));
360	printk("Status.........0x%02x\n", be16_to_cpu(smp->status));
361	if (smp->status)
362		print_status_details(be16_to_cpu(smp->status));
363	printk("Hop pointer....0x%01x\n", smp->hop_ptr);
364	printk("Hop counter....0x%01x\n", smp->hop_cnt);
365	printk("Trans ID.......0x%llx\n",
366		(unsigned long long)be64_to_cpu(smp->tid));
367	printk("Attr ID........0x%02x (%s)\n", be16_to_cpu(smp->attr_id),
368		get_smp_attr(smp->attr_id));
369	printk("Attr modifier..0x%04x\n", be32_to_cpu(smp->attr_mod));
370
371	printk("Mkey...........0x%llx\n",
372		(unsigned long long)be64_to_cpu(smp->mkey));
373	printk("DR SLID........0x%02x\n", be16_to_cpu(smp->dr_slid));
374	printk("DR DLID........0x%02x", be16_to_cpu(smp->dr_dlid));
375
376	if (data) {
377		for (i = 0; i < IB_SMP_DATA_SIZE; i++) {
378			if (i % 16 == 0)
379				printk("\nSMP Data.......");
380			printk("%01x ", smp->data[i]);
381		}
382		for (i = 0; i < IB_SMP_MAX_PATH_HOPS; i++) {
383			if (i % 16 == 0)
384				printk("\nInitial path...");
385			printk("%01x ", smp->initial_path[i]);
386		}
387		for (i = 0; i < IB_SMP_MAX_PATH_HOPS; i++) {
388			if (i % 16 == 0)
389				printk("\nReturn path....");
390			printk("%01x ", smp->return_path[i]);
391		}
392	}
393	printk("\n");
394}
395
396static void snoop_smi_handler(struct ib_mad_agent *mad_agent,
397			      struct ib_mad_send_buf *send_buf,
398			      struct ib_mad_send_wc *mad_send_wc)
399{
400	struct ib_mad_hdr *hdr = send_buf->mad;
401
402	if (!smp && hdr->mgmt_class != mgmt_class)
403		return;
404	if (attr_id && be16_to_cpu(hdr->attr_id) != attr_id)
405		return;
406
407	printk("Madeye:sent SMP\n");
408	print_smp(send_buf->mad);
409}
410
411static void recv_smi_handler(struct ib_mad_agent *mad_agent,
412			     struct ib_mad_recv_wc *mad_recv_wc)
413{
414	if (!smp && mad_recv_wc->recv_buf.mad->mad_hdr.mgmt_class != mgmt_class)
415		return;
416	if (attr_id && be16_to_cpu(mad_recv_wc->recv_buf.mad->mad_hdr.attr_id) != attr_id)
417		return;
418
419	printk("Madeye:recv SMP\n");
420	print_smp((struct ib_smp *)&mad_recv_wc->recv_buf.mad->mad_hdr);
421}
422
423static int is_rmpp_mad(struct ib_mad_hdr *mad_hdr)
424{
425	if (mad_hdr->mgmt_class == IB_MGMT_CLASS_SUBN_ADM) {
426		switch (mad_hdr->method) {
427		case IB_SA_METHOD_GET_TABLE:
428		case IB_SA_METHOD_GET_TABLE_RESP:
429		case IB_SA_METHOD_GET_MULTI_RESP:
430			return 1;
431		default:
432			break;
433		}
434	} else if ((mad_hdr->mgmt_class >= IB_MGMT_CLASS_VENDOR_RANGE2_START) &&
435		   (mad_hdr->mgmt_class <= IB_MGMT_CLASS_VENDOR_RANGE2_END))
436		return 1;
437
438	return 0;
439}
440
441static void snoop_gsi_handler(struct ib_mad_agent *mad_agent,
442			      struct ib_mad_send_buf *send_buf,
443			      struct ib_mad_send_wc *mad_send_wc)
444{
445	struct ib_mad_hdr *hdr = send_buf->mad;
446
447	if (!gmp && hdr->mgmt_class != mgmt_class)
448		return;
449	if (attr_id && be16_to_cpu(hdr->attr_id) != attr_id)
450		return;
451
452	printk("Madeye:sent GMP\n");
453	print_mad_hdr(hdr);
454
455	if (is_rmpp_mad(hdr))
456		print_rmpp_hdr(&((struct ib_rmpp_mad *) hdr)->rmpp_hdr);
457}
458
459static void recv_gsi_handler(struct ib_mad_agent *mad_agent,
460			     struct ib_mad_recv_wc *mad_recv_wc)
461{
462	struct ib_mad_hdr *hdr = &mad_recv_wc->recv_buf.mad->mad_hdr;
463	struct ib_rmpp_mad *mad = NULL;
464	struct ib_sa_mad *sa_mad;
465	struct ib_vendor_mad *vendor_mad;
466	u8 *mad_data;
467	int i, j;
468
469	if (!gmp && hdr->mgmt_class != mgmt_class)
470		return;
471	if (attr_id && be16_to_cpu(mad_recv_wc->recv_buf.mad->mad_hdr.attr_id) != attr_id)
472		return;
473
474	printk("Madeye:recv GMP\n");
475	print_mad_hdr(hdr);
476
477	if (is_rmpp_mad(hdr)) {
478		mad = (struct ib_rmpp_mad *) hdr;
479		print_rmpp_hdr(&mad->rmpp_hdr);
480	}
481
482	if (data) {
483		if (hdr->mgmt_class == IB_MGMT_CLASS_SUBN_ADM) {
484			j = IB_MGMT_SA_DATA;
485			/* Display SA header */
486			if (is_rmpp_mad(hdr) &&
487			    mad->rmpp_hdr.rmpp_type != IB_MGMT_RMPP_TYPE_DATA)
488				return;
489			sa_mad = (struct ib_sa_mad *)
490				 &mad_recv_wc->recv_buf.mad;
491			mad_data = sa_mad->data;
492		} else {
493			if (is_rmpp_mad(hdr)) {
494				j = IB_MGMT_VENDOR_DATA;
495				/* Display OUI */
496				vendor_mad = (struct ib_vendor_mad *)
497					     &mad_recv_wc->recv_buf.mad;
498				printk("Vendor OUI......%01x %01x %01x\n",
499					vendor_mad->oui[0],
500					vendor_mad->oui[1],
501					vendor_mad->oui[2]);
502				mad_data = vendor_mad->data;
503			} else {
504				j = IB_MGMT_MAD_DATA;
505				mad_data = mad_recv_wc->recv_buf.mad->data;
506			}
507		}
508		for (i = 0; i < j; i++) {
509			if (i % 16 == 0)
510				printk("\nData...........");
511			printk("%01x ", mad_data[i]);
512		}
513		printk("\n");
514	}
515}
516
517static void madeye_add_one(struct ib_device *device)
518{
519	struct madeye_port *port;
520	int reg_flags;
521	u8 i, s, e;
522
523	if (device->node_type == RDMA_NODE_IB_SWITCH) {
524		s = 0;
525		e = 0;
526	} else {
527		s = 1;
528		e = device->phys_port_cnt;
529	}
530
531	port = kmalloc(sizeof *port * (e - s + 1), GFP_KERNEL);
532	if (!port)
533		goto out;
534
535	reg_flags = IB_MAD_SNOOP_SEND_COMPLETIONS | IB_MAD_SNOOP_RECVS;
536	for (i = 0; i <= e - s; i++) {
537		port[i].smi_agent = ib_register_mad_snoop(device, i + s,
538							  IB_QPT_SMI,
539							  reg_flags,
540							  snoop_smi_handler,
541							  recv_smi_handler,
542							  &port[i]);
543		port[i].gsi_agent = ib_register_mad_snoop(device, i + s,
544							  IB_QPT_GSI,
545							  reg_flags,
546							  snoop_gsi_handler,
547							  recv_gsi_handler,
548							  &port[i]);
549	}
550
551out:
552	ib_set_client_data(device, &madeye_client, port);
553}
554
555static void madeye_remove_one(struct ib_device *device)
556{
557	struct madeye_port *port;
558	int i, s, e;
559
560	port = (struct madeye_port *)
561		ib_get_client_data(device, &madeye_client);
562	if (!port)
563		return;
564
565	if (device->node_type == RDMA_NODE_IB_SWITCH) {
566		s = 0;
567		e = 0;
568	} else {
569		s = 1;
570		e = device->phys_port_cnt;
571	}
572
573	for (i = 0; i <= e - s; i++) {
574		if (!IS_ERR(port[i].smi_agent))
575			ib_unregister_mad_agent(port[i].smi_agent);
576		if (!IS_ERR(port[i].gsi_agent))
577			ib_unregister_mad_agent(port[i].gsi_agent);
578	}
579	kfree(port);
580}
581
582static int __init ib_madeye_init(void)
583{
584	return ib_register_client(&madeye_client);
585}
586
587static void __exit ib_madeye_cleanup(void)
588{
589	ib_unregister_client(&madeye_client);
590}
591
592module_init(ib_madeye_init);
593module_exit(ib_madeye_cleanup);
594