1 /* 2 * Copyright (c) 2009 Oracle. All rights reserved. 3 * 4 * This software is available to you under a choice of one of two 5 * licenses. You may choose to be licensed under the terms of the GNU 6 * General Public License (GPL) Version 2, available from the file 7 * COPYING in the main directory of this source tree, or the 8 * OpenIB.org BSD license below: 9 * 10 * Redistribution and use in source and binary forms, with or 11 * without modification, are permitted provided that the following 12 * conditions are met: 13 * 14 * - Redistributions of source code must retain the above 15 * copyright notice, this list of conditions and the following 16 * disclaimer. 17 * 18 * - Redistributions in binary form must reproduce the above 19 * copyright notice, this list of conditions and the following 20 * disclaimer in the documentation and/or other materials 21 * provided with the distribution. 22 * 23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 27 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 28 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 29 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 30 * SOFTWARE. 31 * 32 */ 33 #include <linux/module.h> 34 #include <rdma/rdma_cm.h> 35 36 #include "rdma_transport.h" 37 38 static struct rdma_cm_id *rds_rdma_listen_id; 39 40 static char *rds_cm_event_strings[] = { 41 #define RDS_CM_EVENT_STRING(foo) \ 42 [RDMA_CM_EVENT_##foo] = __stringify(RDMA_CM_EVENT_##foo) 43 RDS_CM_EVENT_STRING(ADDR_RESOLVED), 44 RDS_CM_EVENT_STRING(ADDR_ERROR), 45 RDS_CM_EVENT_STRING(ROUTE_RESOLVED), 46 RDS_CM_EVENT_STRING(ROUTE_ERROR), 47 RDS_CM_EVENT_STRING(CONNECT_REQUEST), 48 RDS_CM_EVENT_STRING(CONNECT_RESPONSE), 49 RDS_CM_EVENT_STRING(CONNECT_ERROR), 50 RDS_CM_EVENT_STRING(UNREACHABLE), 51 RDS_CM_EVENT_STRING(REJECTED), 52 RDS_CM_EVENT_STRING(ESTABLISHED), 53 RDS_CM_EVENT_STRING(DISCONNECTED), 54 RDS_CM_EVENT_STRING(DEVICE_REMOVAL), 55 RDS_CM_EVENT_STRING(MULTICAST_JOIN), 56 RDS_CM_EVENT_STRING(MULTICAST_ERROR), 57 RDS_CM_EVENT_STRING(ADDR_CHANGE), 58 RDS_CM_EVENT_STRING(TIMEWAIT_EXIT), 59 #undef RDS_CM_EVENT_STRING 60 }; 61 62 static char *rds_cm_event_str(enum rdma_cm_event_type type) 63 { 64 return rds_str_array(rds_cm_event_strings, 65 ARRAY_SIZE(rds_cm_event_strings), type); 66 }; 67 68 int rds_rdma_cm_event_handler(struct rdma_cm_id *cm_id, 69 struct rdma_cm_event *event) 70 { 71 /* this can be null in the listening path */ 72 struct rds_connection *conn = cm_id->context; 73 struct rds_transport *trans; 74 int ret = 0; 75 76 rdsdebug("conn %p id %p handling event %u (%s)\n", conn, cm_id, 77 event->event, rds_cm_event_str(event->event)); 78 79 if (cm_id->device->node_type == RDMA_NODE_RNIC) 80 trans = &rds_iw_transport; 81 else 82 trans = &rds_ib_transport; 83 84 /* Prevent shutdown from tearing down the connection 85 * while we're executing. */ 86 if (conn) { 87 mutex_lock(&conn->c_cm_lock); 88 89 /* If the connection is being shut down, bail out 90 * right away. We return 0 so cm_id doesn't get 91 * destroyed prematurely */ 92 if (rds_conn_state(conn) == RDS_CONN_DISCONNECTING) { 93 /* Reject incoming connections while we're tearing 94 * down an existing one. */ 95 if (event->event == RDMA_CM_EVENT_CONNECT_REQUEST) 96 ret = 1; 97 goto out; 98 } 99 } 100 101 switch (event->event) { 102 case RDMA_CM_EVENT_CONNECT_REQUEST: 103 ret = trans->cm_handle_connect(cm_id, event); 104 break; 105 106 case RDMA_CM_EVENT_ADDR_RESOLVED: 107 /* XXX do we need to clean up if this fails? */ 108 ret = rdma_resolve_route(cm_id, 109 RDS_RDMA_RESOLVE_TIMEOUT_MS); 110 break; 111 112 case RDMA_CM_EVENT_ROUTE_RESOLVED: 113 /* XXX worry about racing with listen acceptance */ 114 ret = trans->cm_initiate_connect(cm_id); 115 break; 116 117 case RDMA_CM_EVENT_ESTABLISHED: 118 trans->cm_connect_complete(conn, event); 119 break; 120 121 case RDMA_CM_EVENT_ADDR_ERROR: 122 case RDMA_CM_EVENT_ROUTE_ERROR: 123 case RDMA_CM_EVENT_CONNECT_ERROR: 124 case RDMA_CM_EVENT_UNREACHABLE: 125 case RDMA_CM_EVENT_REJECTED: 126 case RDMA_CM_EVENT_DEVICE_REMOVAL: 127 case RDMA_CM_EVENT_ADDR_CHANGE: 128 if (conn) 129 rds_conn_drop(conn); 130 break; 131 132 case RDMA_CM_EVENT_DISCONNECTED: 133 rdsdebug("DISCONNECT event - dropping connection " 134 "%pI4->%pI4\n", &conn->c_laddr, 135 &conn->c_faddr); 136 rds_conn_drop(conn); 137 break; 138 139 default: 140 /* things like device disconnect? */ 141 printk(KERN_ERR "RDS: unknown event %u (%s)!\n", 142 event->event, rds_cm_event_str(event->event)); 143 break; 144 } 145 146 out: 147 if (conn) 148 mutex_unlock(&conn->c_cm_lock); 149 150 rdsdebug("id %p event %u (%s) handling ret %d\n", cm_id, event->event, 151 rds_cm_event_str(event->event), ret); 152 153 return ret; 154 } 155 156 static int rds_rdma_listen_init(void) 157 { 158 struct sockaddr_in sin; 159 struct rdma_cm_id *cm_id; 160 int ret; 161 162 cm_id = rdma_create_id(rds_rdma_cm_event_handler, NULL, RDMA_PS_TCP, 163 IB_QPT_RC); 164 if (IS_ERR(cm_id)) { 165 ret = PTR_ERR(cm_id); 166 printk(KERN_ERR "RDS/RDMA: failed to setup listener, " 167 "rdma_create_id() returned %d\n", ret); 168 return ret; 169 } 170 171 sin.sin_family = AF_INET; 172 sin.sin_addr.s_addr = (__force u32)htonl(INADDR_ANY); 173 sin.sin_port = (__force u16)htons(RDS_PORT); 174 175 /* 176 * XXX I bet this binds the cm_id to a device. If we want to support 177 * fail-over we'll have to take this into consideration. 178 */ 179 ret = rdma_bind_addr(cm_id, (struct sockaddr *)&sin); 180 if (ret) { 181 printk(KERN_ERR "RDS/RDMA: failed to setup listener, " 182 "rdma_bind_addr() returned %d\n", ret); 183 goto out; 184 } 185 186 ret = rdma_listen(cm_id, 128); 187 if (ret) { 188 printk(KERN_ERR "RDS/RDMA: failed to setup listener, " 189 "rdma_listen() returned %d\n", ret); 190 goto out; 191 } 192 193 rdsdebug("cm %p listening on port %u\n", cm_id, RDS_PORT); 194 195 rds_rdma_listen_id = cm_id; 196 cm_id = NULL; 197 out: 198 if (cm_id) 199 rdma_destroy_id(cm_id); 200 return ret; 201 } 202 203 static void rds_rdma_listen_stop(void) 204 { 205 if (rds_rdma_listen_id) { 206 rdsdebug("cm %p\n", rds_rdma_listen_id); 207 rdma_destroy_id(rds_rdma_listen_id); 208 rds_rdma_listen_id = NULL; 209 } 210 } 211 212 static int rds_rdma_init(void) 213 { 214 int ret; 215 216 ret = rds_rdma_listen_init(); 217 if (ret) 218 goto out; 219 220 ret = rds_iw_init(); 221 if (ret) 222 goto err_iw_init; 223 224 ret = rds_ib_init(); 225 if (ret) 226 goto err_ib_init; 227 228 goto out; 229 230 err_ib_init: 231 rds_iw_exit(); 232 err_iw_init: 233 rds_rdma_listen_stop(); 234 out: 235 return ret; 236 } 237 module_init(rds_rdma_init); 238 239 static void rds_rdma_exit(void) 240 { 241 /* stop listening first to ensure no new connections are attempted */ 242 rds_rdma_listen_stop(); 243 rds_ib_exit(); 244 rds_iw_exit(); 245 } 246 module_exit(rds_rdma_exit); 247 248 MODULE_AUTHOR("Oracle Corporation <rds-devel@oss.oracle.com>"); 249 MODULE_DESCRIPTION("RDS: IB/iWARP transport"); 250 MODULE_LICENSE("Dual BSD/GPL"); 251 252