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