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