xref: /openbmc/linux/net/rds/rdma_transport.c (revision f1d95df0)
155b7ed0bSAndy Grover /*
2eee2fa6aSKa-Cheong Poon  * Copyright (c) 2009, 2018 Oracle and/or its affiliates. All rights reserved.
355b7ed0bSAndy Grover  *
455b7ed0bSAndy Grover  * This software is available to you under a choice of one of two
555b7ed0bSAndy Grover  * licenses.  You may choose to be licensed under the terms of the GNU
655b7ed0bSAndy Grover  * General Public License (GPL) Version 2, available from the file
755b7ed0bSAndy Grover  * COPYING in the main directory of this source tree, or the
855b7ed0bSAndy Grover  * OpenIB.org BSD license below:
955b7ed0bSAndy Grover  *
1055b7ed0bSAndy Grover  *     Redistribution and use in source and binary forms, with or
1155b7ed0bSAndy Grover  *     without modification, are permitted provided that the following
1255b7ed0bSAndy Grover  *     conditions are met:
1355b7ed0bSAndy Grover  *
1455b7ed0bSAndy Grover  *      - Redistributions of source code must retain the above
1555b7ed0bSAndy Grover  *        copyright notice, this list of conditions and the following
1655b7ed0bSAndy Grover  *        disclaimer.
1755b7ed0bSAndy Grover  *
1855b7ed0bSAndy Grover  *      - Redistributions in binary form must reproduce the above
1955b7ed0bSAndy Grover  *        copyright notice, this list of conditions and the following
2055b7ed0bSAndy Grover  *        disclaimer in the documentation and/or other materials
2155b7ed0bSAndy Grover  *        provided with the distribution.
2255b7ed0bSAndy Grover  *
2355b7ed0bSAndy Grover  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
2455b7ed0bSAndy Grover  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
2555b7ed0bSAndy Grover  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
2655b7ed0bSAndy Grover  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
2755b7ed0bSAndy Grover  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
2855b7ed0bSAndy Grover  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
2955b7ed0bSAndy Grover  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
3055b7ed0bSAndy Grover  * SOFTWARE.
3155b7ed0bSAndy Grover  *
3255b7ed0bSAndy Grover  */
333a9a231dSPaul Gortmaker #include <linux/module.h>
3455b7ed0bSAndy Grover #include <rdma/rdma_cm.h>
3555b7ed0bSAndy Grover 
360cb43965SSowmini Varadhan #include "rds_single_path.h"
3755b7ed0bSAndy Grover #include "rdma_transport.h"
38ae05368aSsantosh.shilimkar@oracle.com #include "ib.h"
3955b7ed0bSAndy Grover 
401e2b44e7SKa-Cheong Poon /* Global IPv4 and IPv6 RDS RDMA listener cm_id */
4111bc9421SAndy Grover static struct rdma_cm_id *rds_rdma_listen_id;
42e65d4d96SKa-Cheong Poon #if IS_ENABLED(CONFIG_IPV6)
431e2b44e7SKa-Cheong Poon static struct rdma_cm_id *rds6_rdma_listen_id;
44e65d4d96SKa-Cheong Poon #endif
4555b7ed0bSAndy Grover 
46e0e6d062SZhu Yanjun /* Per IB specification 7.7.3, service level is a 4-bit field. */
47e0e6d062SZhu Yanjun #define TOS_TO_SL(tos)		((tos) & 0xF)
48e0e6d062SZhu Yanjun 
rds_rdma_cm_event_handler_cmn(struct rdma_cm_id * cm_id,struct rdma_cm_event * event,bool isv6)49eee2fa6aSKa-Cheong Poon static int rds_rdma_cm_event_handler_cmn(struct rdma_cm_id *cm_id,
50eee2fa6aSKa-Cheong Poon 					 struct rdma_cm_event *event,
51eee2fa6aSKa-Cheong Poon 					 bool isv6)
5255b7ed0bSAndy Grover {
5355b7ed0bSAndy Grover 	/* this can be null in the listening path */
5455b7ed0bSAndy Grover 	struct rds_connection *conn = cm_id->context;
5555b7ed0bSAndy Grover 	struct rds_transport *trans;
5655b7ed0bSAndy Grover 	int ret = 0;
57d021fabfSSantosh Shilimkar 	int *err;
58d021fabfSSantosh Shilimkar 	u8 len;
5955b7ed0bSAndy Grover 
6059f740a6SZach Brown 	rdsdebug("conn %p id %p handling event %u (%s)\n", conn, cm_id,
613c88f3dcSSagi Grimberg 		 event->event, rdma_event_msg(event->event));
6255b7ed0bSAndy Grover 
63dcdede04Ssantosh.shilimkar@oracle.com 	if (cm_id->device->node_type == RDMA_NODE_IB_CA)
6455b7ed0bSAndy Grover 		trans = &rds_ib_transport;
6555b7ed0bSAndy Grover 
6655b7ed0bSAndy Grover 	/* Prevent shutdown from tearing down the connection
6755b7ed0bSAndy Grover 	 * while we're executing. */
6855b7ed0bSAndy Grover 	if (conn) {
6955b7ed0bSAndy Grover 		mutex_lock(&conn->c_cm_lock);
7055b7ed0bSAndy Grover 
7155b7ed0bSAndy Grover 		/* If the connection is being shut down, bail out
7255b7ed0bSAndy Grover 		 * right away. We return 0 so cm_id doesn't get
7355b7ed0bSAndy Grover 		 * destroyed prematurely */
7455b7ed0bSAndy Grover 		if (rds_conn_state(conn) == RDS_CONN_DISCONNECTING) {
7555b7ed0bSAndy Grover 			/* Reject incoming connections while we're tearing
7655b7ed0bSAndy Grover 			 * down an existing one. */
7755b7ed0bSAndy Grover 			if (event->event == RDMA_CM_EVENT_CONNECT_REQUEST)
7855b7ed0bSAndy Grover 				ret = 1;
7955b7ed0bSAndy Grover 			goto out;
8055b7ed0bSAndy Grover 		}
8155b7ed0bSAndy Grover 	}
8255b7ed0bSAndy Grover 
8355b7ed0bSAndy Grover 	switch (event->event) {
8455b7ed0bSAndy Grover 	case RDMA_CM_EVENT_CONNECT_REQUEST:
85eee2fa6aSKa-Cheong Poon 		ret = trans->cm_handle_connect(cm_id, event, isv6);
8655b7ed0bSAndy Grover 		break;
8755b7ed0bSAndy Grover 
8855b7ed0bSAndy Grover 	case RDMA_CM_EVENT_ADDR_RESOLVED:
89*f1d95df0SArtem Chernyshev 		if (conn) {
90fd261ce6SSantosh Shilimkar 			rdma_set_service_type(cm_id, conn->c_tos);
915aa54bd2SHåkon Bugge 			rdma_set_min_rnr_timer(cm_id, IB_RNR_TIMER_000_32);
9255b7ed0bSAndy Grover 			/* XXX do we need to clean up if this fails? */
9355b7ed0bSAndy Grover 			ret = rdma_resolve_route(cm_id,
9455b7ed0bSAndy Grover 						 RDS_RDMA_RESOLVE_TIMEOUT_MS);
95*f1d95df0SArtem Chernyshev 		}
9655b7ed0bSAndy Grover 		break;
9755b7ed0bSAndy Grover 
9855b7ed0bSAndy Grover 	case RDMA_CM_EVENT_ROUTE_RESOLVED:
99ae05368aSsantosh.shilimkar@oracle.com 		/* Connection could have been dropped so make sure the
100ae05368aSsantosh.shilimkar@oracle.com 		 * cm_id is valid before proceeding
101ae05368aSsantosh.shilimkar@oracle.com 		 */
102ae05368aSsantosh.shilimkar@oracle.com 		if (conn) {
103ae05368aSsantosh.shilimkar@oracle.com 			struct rds_ib_connection *ibic;
104ae05368aSsantosh.shilimkar@oracle.com 
105ae05368aSsantosh.shilimkar@oracle.com 			ibic = conn->c_transport_data;
106e0e6d062SZhu Yanjun 			if (ibic && ibic->i_cm_id == cm_id) {
107e0e6d062SZhu Yanjun 				cm_id->route.path_rec[0].sl =
108e0e6d062SZhu Yanjun 					TOS_TO_SL(conn->c_tos);
109eee2fa6aSKa-Cheong Poon 				ret = trans->cm_initiate_connect(cm_id, isv6);
110e0e6d062SZhu Yanjun 			} else {
111ae05368aSsantosh.shilimkar@oracle.com 				rds_conn_drop(conn);
112ae05368aSsantosh.shilimkar@oracle.com 			}
113e0e6d062SZhu Yanjun 		}
11455b7ed0bSAndy Grover 		break;
11555b7ed0bSAndy Grover 
11655b7ed0bSAndy Grover 	case RDMA_CM_EVENT_ESTABLISHED:
117c7ba50feSJia-Ju Bai 		if (conn)
11855b7ed0bSAndy Grover 			trans->cm_connect_complete(conn, event);
11955b7ed0bSAndy Grover 		break;
12055b7ed0bSAndy Grover 
12139384f04SSteve Wise 	case RDMA_CM_EVENT_REJECTED:
122d021fabfSSantosh Shilimkar 		if (!conn)
123d021fabfSSantosh Shilimkar 			break;
124d021fabfSSantosh Shilimkar 		err = (int *)rdma_consumer_reject_data(cm_id, event, &len);
1258c6166cfSGerd Rausch 		if (!err ||
1268c6166cfSGerd Rausch 		    (err && len >= sizeof(*err) &&
1278c6166cfSGerd Rausch 		     ((*err) <= RDS_RDMA_REJ_INCOMPAT))) {
128d021fabfSSantosh Shilimkar 			pr_warn("RDS/RDMA: conn <%pI6c, %pI6c> rejected, dropping connection\n",
129d021fabfSSantosh Shilimkar 				&conn->c_laddr, &conn->c_faddr);
130dc205a8dSSantosh Shilimkar 
131dc205a8dSSantosh Shilimkar 			if (!conn->c_tos)
132d021fabfSSantosh Shilimkar 				conn->c_proposed_version = RDS_PROTOCOL_COMPAT_VERSION;
133dc205a8dSSantosh Shilimkar 
134d021fabfSSantosh Shilimkar 			rds_conn_drop(conn);
135d021fabfSSantosh Shilimkar 		}
13639384f04SSteve Wise 		rdsdebug("Connection rejected: %s\n",
13739384f04SSteve Wise 			 rdma_reject_msg(cm_id, event->status));
138d021fabfSSantosh Shilimkar 		break;
13955b7ed0bSAndy Grover 	case RDMA_CM_EVENT_ADDR_ERROR:
14055b7ed0bSAndy Grover 	case RDMA_CM_EVENT_ROUTE_ERROR:
14155b7ed0bSAndy Grover 	case RDMA_CM_EVENT_CONNECT_ERROR:
14255b7ed0bSAndy Grover 	case RDMA_CM_EVENT_UNREACHABLE:
14355b7ed0bSAndy Grover 	case RDMA_CM_EVENT_DEVICE_REMOVAL:
14455b7ed0bSAndy Grover 	case RDMA_CM_EVENT_ADDR_CHANGE:
14555b7ed0bSAndy Grover 		if (conn)
14655b7ed0bSAndy Grover 			rds_conn_drop(conn);
14755b7ed0bSAndy Grover 		break;
14855b7ed0bSAndy Grover 
14955b7ed0bSAndy Grover 	case RDMA_CM_EVENT_DISCONNECTED:
150c7ba50feSJia-Ju Bai 		if (!conn)
151c7ba50feSJia-Ju Bai 			break;
15297069788SAndy Grover 		rdsdebug("DISCONNECT event - dropping connection "
153eee2fa6aSKa-Cheong Poon 			 "%pI6c->%pI6c\n", &conn->c_laddr,
15455b7ed0bSAndy Grover 			 &conn->c_faddr);
15555b7ed0bSAndy Grover 		rds_conn_drop(conn);
15655b7ed0bSAndy Grover 		break;
15755b7ed0bSAndy Grover 
15837ea401eSsantosh.shilimkar@oracle.com 	case RDMA_CM_EVENT_TIMEWAIT_EXIT:
15937ea401eSsantosh.shilimkar@oracle.com 		if (conn) {
160eee2fa6aSKa-Cheong Poon 			pr_info("RDS: RDMA_CM_EVENT_TIMEWAIT_EXIT event: dropping connection %pI6c->%pI6c\n",
16137ea401eSsantosh.shilimkar@oracle.com 				&conn->c_laddr, &conn->c_faddr);
16237ea401eSsantosh.shilimkar@oracle.com 			rds_conn_drop(conn);
16337ea401eSsantosh.shilimkar@oracle.com 		}
16437ea401eSsantosh.shilimkar@oracle.com 		break;
16537ea401eSsantosh.shilimkar@oracle.com 
16655b7ed0bSAndy Grover 	default:
16755b7ed0bSAndy Grover 		/* things like device disconnect? */
16859f740a6SZach Brown 		printk(KERN_ERR "RDS: unknown event %u (%s)!\n",
1693c88f3dcSSagi Grimberg 		       event->event, rdma_event_msg(event->event));
17055b7ed0bSAndy Grover 		break;
17155b7ed0bSAndy Grover 	}
17255b7ed0bSAndy Grover 
17355b7ed0bSAndy Grover out:
17455b7ed0bSAndy Grover 	if (conn)
17555b7ed0bSAndy Grover 		mutex_unlock(&conn->c_cm_lock);
17655b7ed0bSAndy Grover 
17759f740a6SZach Brown 	rdsdebug("id %p event %u (%s) handling ret %d\n", cm_id, event->event,
1783c88f3dcSSagi Grimberg 		 rdma_event_msg(event->event), ret);
17955b7ed0bSAndy Grover 
18055b7ed0bSAndy Grover 	return ret;
18155b7ed0bSAndy Grover }
18255b7ed0bSAndy Grover 
rds_rdma_cm_event_handler(struct rdma_cm_id * cm_id,struct rdma_cm_event * event)183eee2fa6aSKa-Cheong Poon int rds_rdma_cm_event_handler(struct rdma_cm_id *cm_id,
184eee2fa6aSKa-Cheong Poon 			      struct rdma_cm_event *event)
18555b7ed0bSAndy Grover {
186eee2fa6aSKa-Cheong Poon 	return rds_rdma_cm_event_handler_cmn(cm_id, event, false);
187eee2fa6aSKa-Cheong Poon }
188eee2fa6aSKa-Cheong Poon 
189e65d4d96SKa-Cheong Poon #if IS_ENABLED(CONFIG_IPV6)
rds6_rdma_cm_event_handler(struct rdma_cm_id * cm_id,struct rdma_cm_event * event)1901e2b44e7SKa-Cheong Poon int rds6_rdma_cm_event_handler(struct rdma_cm_id *cm_id,
1911e2b44e7SKa-Cheong Poon 			       struct rdma_cm_event *event)
1921e2b44e7SKa-Cheong Poon {
1931e2b44e7SKa-Cheong Poon 	return rds_rdma_cm_event_handler_cmn(cm_id, event, true);
1941e2b44e7SKa-Cheong Poon }
195e65d4d96SKa-Cheong Poon #endif
1961e2b44e7SKa-Cheong Poon 
rds_rdma_listen_init_common(rdma_cm_event_handler handler,struct sockaddr * sa,struct rdma_cm_id ** ret_cm_id)197eee2fa6aSKa-Cheong Poon static int rds_rdma_listen_init_common(rdma_cm_event_handler handler,
198eee2fa6aSKa-Cheong Poon 				       struct sockaddr *sa,
199eee2fa6aSKa-Cheong Poon 				       struct rdma_cm_id **ret_cm_id)
200eee2fa6aSKa-Cheong Poon {
20155b7ed0bSAndy Grover 	struct rdma_cm_id *cm_id;
20255b7ed0bSAndy Grover 	int ret;
20355b7ed0bSAndy Grover 
204eee2fa6aSKa-Cheong Poon 	cm_id = rdma_create_id(&init_net, handler, NULL,
205fa20105eSGuy Shapiro 			       RDMA_PS_TCP, IB_QPT_RC);
20655b7ed0bSAndy Grover 	if (IS_ERR(cm_id)) {
20755b7ed0bSAndy Grover 		ret = PTR_ERR(cm_id);
20892c330b9SAndy Grover 		printk(KERN_ERR "RDS/RDMA: failed to setup listener, "
20955b7ed0bSAndy Grover 		       "rdma_create_id() returned %d\n", ret);
21024acc689SDan Carpenter 		return ret;
21155b7ed0bSAndy Grover 	}
21255b7ed0bSAndy Grover 
21355b7ed0bSAndy Grover 	/*
21455b7ed0bSAndy Grover 	 * XXX I bet this binds the cm_id to a device.  If we want to support
21555b7ed0bSAndy Grover 	 * fail-over we'll have to take this into consideration.
21655b7ed0bSAndy Grover 	 */
217eee2fa6aSKa-Cheong Poon 	ret = rdma_bind_addr(cm_id, sa);
21855b7ed0bSAndy Grover 	if (ret) {
21992c330b9SAndy Grover 		printk(KERN_ERR "RDS/RDMA: failed to setup listener, "
22055b7ed0bSAndy Grover 		       "rdma_bind_addr() returned %d\n", ret);
22155b7ed0bSAndy Grover 		goto out;
22255b7ed0bSAndy Grover 	}
22355b7ed0bSAndy Grover 
22455b7ed0bSAndy Grover 	ret = rdma_listen(cm_id, 128);
22555b7ed0bSAndy Grover 	if (ret) {
22692c330b9SAndy Grover 		printk(KERN_ERR "RDS/RDMA: failed to setup listener, "
22755b7ed0bSAndy Grover 		       "rdma_listen() returned %d\n", ret);
22855b7ed0bSAndy Grover 		goto out;
22955b7ed0bSAndy Grover 	}
23055b7ed0bSAndy Grover 
23155b7ed0bSAndy Grover 	rdsdebug("cm %p listening on port %u\n", cm_id, RDS_PORT);
23255b7ed0bSAndy Grover 
233eee2fa6aSKa-Cheong Poon 	*ret_cm_id = cm_id;
23455b7ed0bSAndy Grover 	cm_id = NULL;
23555b7ed0bSAndy Grover out:
23655b7ed0bSAndy Grover 	if (cm_id)
23755b7ed0bSAndy Grover 		rdma_destroy_id(cm_id);
23855b7ed0bSAndy Grover 	return ret;
23955b7ed0bSAndy Grover }
24055b7ed0bSAndy Grover 
241eee2fa6aSKa-Cheong Poon /* Initialize the RDS RDMA listeners.  We create two listeners for
242eee2fa6aSKa-Cheong Poon  * compatibility reason.  The one on RDS_PORT is used for IPv4
243eee2fa6aSKa-Cheong Poon  * requests only.  The one on RDS_CM_PORT is used for IPv6 requests
244eee2fa6aSKa-Cheong Poon  * only.  So only IPv6 enabled RDS module will communicate using this
245eee2fa6aSKa-Cheong Poon  * port.
246eee2fa6aSKa-Cheong Poon  */
rds_rdma_listen_init(void)247eee2fa6aSKa-Cheong Poon static int rds_rdma_listen_init(void)
248eee2fa6aSKa-Cheong Poon {
249eee2fa6aSKa-Cheong Poon 	int ret;
250e65d4d96SKa-Cheong Poon #if IS_ENABLED(CONFIG_IPV6)
2511e2b44e7SKa-Cheong Poon 	struct sockaddr_in6 sin6;
252e65d4d96SKa-Cheong Poon #endif
253eee2fa6aSKa-Cheong Poon 	struct sockaddr_in sin;
254eee2fa6aSKa-Cheong Poon 
255eee2fa6aSKa-Cheong Poon 	sin.sin_family = PF_INET;
256eee2fa6aSKa-Cheong Poon 	sin.sin_addr.s_addr = htonl(INADDR_ANY);
257eee2fa6aSKa-Cheong Poon 	sin.sin_port = htons(RDS_PORT);
258eee2fa6aSKa-Cheong Poon 	ret = rds_rdma_listen_init_common(rds_rdma_cm_event_handler,
259eee2fa6aSKa-Cheong Poon 					  (struct sockaddr *)&sin,
260eee2fa6aSKa-Cheong Poon 					  &rds_rdma_listen_id);
2611e2b44e7SKa-Cheong Poon 	if (ret != 0)
262eee2fa6aSKa-Cheong Poon 		return ret;
2631e2b44e7SKa-Cheong Poon 
264e65d4d96SKa-Cheong Poon #if IS_ENABLED(CONFIG_IPV6)
2651e2b44e7SKa-Cheong Poon 	sin6.sin6_family = PF_INET6;
2661e2b44e7SKa-Cheong Poon 	sin6.sin6_addr = in6addr_any;
2671e2b44e7SKa-Cheong Poon 	sin6.sin6_port = htons(RDS_CM_PORT);
2681e2b44e7SKa-Cheong Poon 	sin6.sin6_scope_id = 0;
2691e2b44e7SKa-Cheong Poon 	sin6.sin6_flowinfo = 0;
2701e2b44e7SKa-Cheong Poon 	ret = rds_rdma_listen_init_common(rds6_rdma_cm_event_handler,
2711e2b44e7SKa-Cheong Poon 					  (struct sockaddr *)&sin6,
2721e2b44e7SKa-Cheong Poon 					  &rds6_rdma_listen_id);
2731e2b44e7SKa-Cheong Poon 	/* Keep going even when IPv6 is not enabled in the system. */
2741e2b44e7SKa-Cheong Poon 	if (ret != 0)
2751e2b44e7SKa-Cheong Poon 		rdsdebug("Cannot set up IPv6 RDMA listener\n");
276e65d4d96SKa-Cheong Poon #endif
2771e2b44e7SKa-Cheong Poon 	return 0;
278eee2fa6aSKa-Cheong Poon }
279eee2fa6aSKa-Cheong Poon 
rds_rdma_listen_stop(void)28055b7ed0bSAndy Grover static void rds_rdma_listen_stop(void)
28155b7ed0bSAndy Grover {
28211bc9421SAndy Grover 	if (rds_rdma_listen_id) {
28311bc9421SAndy Grover 		rdsdebug("cm %p\n", rds_rdma_listen_id);
28411bc9421SAndy Grover 		rdma_destroy_id(rds_rdma_listen_id);
28511bc9421SAndy Grover 		rds_rdma_listen_id = NULL;
28655b7ed0bSAndy Grover 	}
287e65d4d96SKa-Cheong Poon #if IS_ENABLED(CONFIG_IPV6)
2881e2b44e7SKa-Cheong Poon 	if (rds6_rdma_listen_id) {
2891e2b44e7SKa-Cheong Poon 		rdsdebug("cm %p\n", rds6_rdma_listen_id);
2901e2b44e7SKa-Cheong Poon 		rdma_destroy_id(rds6_rdma_listen_id);
2911e2b44e7SKa-Cheong Poon 		rds6_rdma_listen_id = NULL;
2921e2b44e7SKa-Cheong Poon 	}
293e65d4d96SKa-Cheong Poon #endif
29455b7ed0bSAndy Grover }
29555b7ed0bSAndy Grover 
rds_rdma_init(void)296f0bd32c8SXiu Jianfeng static int __init rds_rdma_init(void)
29755b7ed0bSAndy Grover {
29855b7ed0bSAndy Grover 	int ret;
29955b7ed0bSAndy Grover 
30055b7ed0bSAndy Grover 	ret = rds_ib_init();
30155b7ed0bSAndy Grover 	if (ret)
30255b7ed0bSAndy Grover 		goto out;
30355b7ed0bSAndy Grover 
3048d5d8a5fSQing Huang 	ret = rds_rdma_listen_init();
3058d5d8a5fSQing Huang 	if (ret)
3068d5d8a5fSQing Huang 		rds_ib_exit();
30755b7ed0bSAndy Grover out:
30855b7ed0bSAndy Grover 	return ret;
30955b7ed0bSAndy Grover }
31040d86609SAndy Grover module_init(rds_rdma_init);
31155b7ed0bSAndy Grover 
rds_rdma_exit(void)312f0bd32c8SXiu Jianfeng static void __exit rds_rdma_exit(void)
31355b7ed0bSAndy Grover {
31455b7ed0bSAndy Grover 	/* stop listening first to ensure no new connections are attempted */
31555b7ed0bSAndy Grover 	rds_rdma_listen_stop();
31655b7ed0bSAndy Grover 	rds_ib_exit();
31755b7ed0bSAndy Grover }
31840d86609SAndy Grover module_exit(rds_rdma_exit);
31940d86609SAndy Grover 
32040d86609SAndy Grover MODULE_AUTHOR("Oracle Corporation <rds-devel@oss.oracle.com>");
321dcdede04Ssantosh.shilimkar@oracle.com MODULE_DESCRIPTION("RDS: IB transport");
32240d86609SAndy Grover MODULE_LICENSE("Dual BSD/GPL");
323