1 /*
2  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
3  */
4 
5 /*
6  * This file contains code imported from the OFED rds source file
7  * rdma_transport.c * Oracle elects to have and use the contents of
8  * rdma_transport.c under and governed by the OpenIB.org BSD license
9  * (see below for full license text). However, the following notice
10  * accompanied the original version of this file:
11  */
12 
13 /*
14  * Copyright (c) 2009 Oracle.  All rights reserved.
15  *
16  * This software is available to you under a choice of one of two
17  * licenses.  You may choose to be licensed under the terms of the GNU
18  * General Public License (GPL) Version 2, available from the file
19  * COPYING in the main directory of this source tree, or the
20  * OpenIB.org BSD license below:
21  *
22  *     Redistribution and use in source and binary forms, with or
23  *     without modification, are permitted provided that the following
24  *     conditions are met:
25  *
26  *      - Redistributions of source code must retain the above
27  *        copyright notice, this list of conditions and the following
28  *        disclaimer.
29  *
30  *      - Redistributions in binary form must reproduce the above
31  *        copyright notice, this list of conditions and the following
32  *        disclaimer in the documentation and/or other materials
33  *        provided with the distribution.
34  *
35  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
36  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
37  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
38  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
39  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
40  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
41  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
42  * SOFTWARE.
43  *
44  */
45 #include <sys/ib/clients/of/rdma/ib_verbs.h>
46 #include <sys/ib/clients/of/rdma/ib_addr.h>
47 #include <sys/ib/clients/of/rdma/rdma_cm.h>
48 
49 #include <sys/ib/clients/rdsv3/ib.h>
50 #include <sys/ib/clients/rdsv3/rdma_transport.h>
51 #include <sys/ib/clients/rdsv3/rdsv3_debug.h>
52 
53 kmutex_t rdsv3_rdma_listen_id_lock;
54 struct rdma_cm_id *rdsv3_rdma_listen_id = NULL;
55 
56 int
rdsv3_rdma_cm_event_handler(struct rdma_cm_id * cm_id,struct rdma_cm_event * event)57 rdsv3_rdma_cm_event_handler(struct rdma_cm_id *cm_id,
58     struct rdma_cm_event *event)
59 {
60 	/* this can be null in the listening path */
61 	struct rdsv3_connection *conn = cm_id->context;
62 	struct rdsv3_transport *trans;
63 	int ret = 0;
64 
65 	RDSV3_DPRINTF2("rdsv3_rdma_cm_event_handler",
66 	    "conn %p id %p handling event %u", conn, cm_id, event->event);
67 
68 	trans = &rdsv3_ib_transport;
69 
70 	/*
71 	 * Prevent shutdown from tearing down the connection
72 	 * while we're executing.
73 	 */
74 	if (conn) {
75 		mutex_enter(&conn->c_cm_lock);
76 
77 		/*
78 		 * If the connection is being shut down, bail out
79 		 * right away. We return 0 so cm_id doesn't get
80 		 * destroyed prematurely
81 		 */
82 		if (rdsv3_conn_state(conn) == RDSV3_CONN_DISCONNECTING) {
83 			/*
84 			 * Reject incoming connections while we're tearing
85 			 * down an existing one.
86 			 */
87 			if (event->event == RDMA_CM_EVENT_CONNECT_REQUEST)
88 				ret = 1;
89 			RDSV3_DPRINTF2("rdsv3_rdma_cm_event_handler",
90 			    "conn %p id %p incoming event %u when "
91 			    "disconnecting", conn, cm_id, event->event);
92 			goto out;
93 		}
94 	}
95 
96 	switch (event->event) {
97 	case RDMA_CM_EVENT_CONNECT_REQUEST:
98 		ret = trans->cm_handle_connect(cm_id, event);
99 		break;
100 
101 	case RDMA_CM_EVENT_ADDR_RESOLVED:
102 		/* XXX do we need to clean up if this fails? */
103 		ret = rdma_resolve_route(cm_id,
104 		    RDSV3_RDMA_RESOLVE_TIMEOUT_MS);
105 		break;
106 
107 	case RDMA_CM_EVENT_ROUTE_RESOLVED:
108 		/* XXX worry about racing with listen acceptance */
109 		ret = trans->cm_initiate_connect(cm_id);
110 		break;
111 
112 	case RDMA_CM_EVENT_ESTABLISHED:
113 		trans->cm_connect_complete(conn, event);
114 		break;
115 
116 	case RDMA_CM_EVENT_ADDR_ERROR:
117 	case RDMA_CM_EVENT_ROUTE_ERROR:
118 	case RDMA_CM_EVENT_CONNECT_ERROR:
119 	case RDMA_CM_EVENT_UNREACHABLE:
120 	case RDMA_CM_EVENT_REJECTED:
121 	case RDMA_CM_EVENT_DEVICE_REMOVAL:
122 	case RDMA_CM_EVENT_ADDR_CHANGE:
123 		if (conn)
124 			rdsv3_conn_drop(conn);
125 		break;
126 
127 	case RDMA_CM_EVENT_DISCONNECTED:
128 		RDSV3_DPRINTF2("rdsv3_rdma_cm_event_handler",
129 		    "RDS/RDMA: DISCONNECT event - dropping connection "
130 		    "cm_id: %p", cm_id);
131 		if (conn) {
132 			RDSV3_DPRINTF2("rdsv3_rdma_cm_event_handler",
133 			    "RDS/RDMA: DISCONNECT event - dropping connection "
134 			    "%u.%u.%u.%u->%u.%u.%u.%u", NIPQUAD(conn->c_laddr),
135 			    NIPQUAD(conn->c_faddr));
136 			rdsv3_conn_drop(conn);
137 		}
138 		break;
139 
140 	default:
141 		/* things like device disconnect? */
142 		RDSV3_DPRINTF2("rdsv3_rdma_cm_event_handler",
143 		    "unknown event %u!\n", event->event);
144 		RDSV3_PANIC();
145 		break;
146 	}
147 
148 out:
149 	if (conn)
150 		mutex_exit(&conn->c_cm_lock);
151 
152 	RDSV3_DPRINTF2("rdsv3_rdma_cm_event_handler",
153 	    "id %p event %u handling ret %d", cm_id, event->event, ret);
154 
155 	return (ret);
156 }
157 
158 static int
rdsv3_rdma_listen_init(void)159 rdsv3_rdma_listen_init(void)
160 {
161 	struct sockaddr_in sin;
162 	struct rdma_cm_id *cm_id;
163 	int ret;
164 
165 	RDSV3_DPRINTF2("rdsv3_rdma_listen_init", "Enter");
166 
167 	cm_id = rdma_create_id(rdsv3_rdma_cm_event_handler, NULL, RDMA_PS_TCP);
168 	if (IS_ERR(cm_id)) {
169 		ret = PTR_ERR(cm_id);
170 		RDSV3_DPRINTF2("rdsv3_rdma_listen_init",
171 		    "RDS/RDMA: failed to setup listener, "
172 		    "rdma_create_id() returned %d", ret);
173 		goto out;
174 	}
175 
176 	sin.sin_family = PF_INET;
177 	sin.sin_addr.s_addr = (uint32_t)htonl(INADDR_ANY);
178 	sin.sin_port = (uint16_t)htons(RDSV3_PORT);
179 
180 	/*
181 	 * XXX I bet this binds the cm_id to a device.  If we want to support
182 	 * fail-over we'll have to take this into consideration.
183 	 */
184 	ret = rdma_bind_addr(cm_id, (struct sockaddr *)&sin);
185 	if (ret) {
186 		RDSV3_DPRINTF2("rdsv3_rdma_listen_init",
187 		    "RDS/RDMA: failed to setup listener, "
188 		    "rdma_bind_addr() returned %d", ret);
189 		goto out;
190 	}
191 
192 	ret = rdma_listen(cm_id, 128);
193 	if (ret) {
194 		RDSV3_DPRINTF2("rdsv3_rdma_listen_init",
195 		    "RDS/RDMA: failed to setup listener, "
196 		    "rdma_listen() returned %d", ret);
197 		goto out;
198 	}
199 
200 	RDSV3_DPRINTF5("rdsv3_rdma_listen_init",
201 	    "cm %p listening on port %u", cm_id, RDSV3_PORT);
202 
203 	rdsv3_rdma_listen_id = cm_id;
204 	cm_id = NULL;
205 
206 	RDSV3_DPRINTF2("rdsv3_rdma_listen_init",
207 	    "Return: rdsv3_rdma_listen_id: %p", rdsv3_rdma_listen_id);
208 out:
209 	if (cm_id)
210 		rdma_destroy_id(cm_id);
211 	return (ret);
212 }
213 
rdsv3_rdma_listen_stop(void)214 static void rdsv3_rdma_listen_stop(void)
215 {
216 	RDSV3_DPRINTF2("rdsv3_rdma_listen_stop", "cm %p", rdsv3_rdma_listen_id);
217 	rdma_destroy_id(rdsv3_rdma_listen_id);
218 	RDSV3_DPRINTF2("rdsv3_rdma_listen_stop", "Return");
219 }
220 
221 /*
222  * This function can be called via two routes.
223  * 	1. During attach on a worker thread.
224  *	2. From rdsv3_create() for 1st socket.
225  */
226 void
rdsv3_rdma_init()227 rdsv3_rdma_init()
228 {
229 	int ret;
230 
231 	RDSV3_DPRINTF2("rdsv3_rdma_init", "Enter");
232 
233 	mutex_enter(&rdsv3_rdma_listen_id_lock);
234 	if (rdsv3_rdma_listen_id != NULL) {
235 		RDSV3_DPRINTF5("rdsv3_rdma_init",
236 		    "rdsv3_rdma_listen_id is already initialized: %p",
237 		    rdsv3_rdma_listen_id);
238 		mutex_exit(&rdsv3_rdma_listen_id_lock);
239 		return;
240 	}
241 
242 	ret = rdsv3_rdma_listen_init();
243 	if (ret) {
244 		mutex_exit(&rdsv3_rdma_listen_id_lock);
245 		return;
246 	}
247 
248 	ret = rdsv3_ib_init();
249 	if (ret) {
250 		rdsv3_rdma_listen_stop();
251 	}
252 	mutex_exit(&rdsv3_rdma_listen_id_lock);
253 
254 	RDSV3_DPRINTF2("rdsv3_rdma_init", "Return");
255 }
256 
257 /*ARGSUSED*/
258 void
rdsv3_rdma_exit(void * arg)259 rdsv3_rdma_exit(void *arg)
260 {
261 	RDSV3_DPRINTF2("rdsv3_rdma_exit", "Enter");
262 
263 	/* stop listening first to ensure no new connections are attempted */
264 	if (rdsv3_rdma_listen_id) {
265 		rdsv3_rdma_listen_stop();
266 		rdsv3_ib_exit();
267 		rdsv3_rdma_listen_id = NULL;
268 	}
269 
270 	RDSV3_DPRINTF2("rdsv3_rdma_exit", "Return");
271 }
272