1178784Skmacy/* 2178784Skmacy * Copyright (c) 2004, 2005 Intel Corporation. All rights reserved. 3178784Skmacy * Copyright (c) 2004 Topspin Corporation. All rights reserved. 4178784Skmacy * Copyright (c) 2004, 2005 Voltaire Corporation. All rights reserved. 5178784Skmacy * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. 6178784Skmacy * Copyright (c) 2005 Open Grid Computing, Inc. All rights reserved. 7178784Skmacy * Copyright (c) 2005 Network Appliance, Inc. All rights reserved. 8178784Skmacy * 9178784Skmacy * This software is available to you under a choice of one of two 10178784Skmacy * licenses. You may choose to be licensed under the terms of the GNU 11178784Skmacy * General Public License (GPL) Version 2, available from the file 12178784Skmacy * COPYING in the main directory of this source tree, or the 13178784Skmacy * OpenIB.org BSD license below: 14178784Skmacy * 15178784Skmacy * Redistribution and use in source and binary forms, with or 16178784Skmacy * without modification, are permitted provided that the following 17178784Skmacy * conditions are met: 18178784Skmacy * 19178784Skmacy * - Redistributions of source code must retain the above 20178784Skmacy * copyright notice, this list of conditions and the following 21178784Skmacy * disclaimer. 22178784Skmacy * 23178784Skmacy * - Redistributions in binary form must reproduce the above 24178784Skmacy * copyright notice, this list of conditions and the following 25178784Skmacy * disclaimer in the documentation and/or other materials 26178784Skmacy * provided with the distribution. 27178784Skmacy * 28178784Skmacy * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 29178784Skmacy * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 30178784Skmacy * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 31178784Skmacy * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 32178784Skmacy * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 33178784Skmacy * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 34178784Skmacy * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 35178784Skmacy * SOFTWARE. 36178784Skmacy * 37178784Skmacy */ 38178784Skmacy 39178784Skmacy#include <sys/cdefs.h> 40178784Skmacy__FBSDID("$FreeBSD$"); 41178784Skmacy 42178784Skmacy#include <sys/types.h> 43178784Skmacy#include <sys/param.h> 44178784Skmacy#include <sys/systm.h> 45178784Skmacy#include <sys/proc.h> 46178784Skmacy#include <sys/kernel.h> 47178784Skmacy#include <sys/libkern.h> 48178784Skmacy#include <sys/socket.h> 49178784Skmacy#include <sys/socketvar.h> 50178784Skmacy#include <sys/module.h> 51178784Skmacy#include <sys/lock.h> 52178784Skmacy#include <sys/mutex.h> 53178784Skmacy#include <sys/rwlock.h> 54178784Skmacy#include <sys/queue.h> 55178784Skmacy#include <sys/taskqueue.h> 56178784Skmacy#include <sys/priv.h> 57178784Skmacy#include <sys/syslog.h> 58178784Skmacy#include <sys/malloc.h> 59178784Skmacy 60178784Skmacy#include <netinet/in.h> 61178784Skmacy#include <netinet/in_pcb.h> 62178784Skmacy 63178784Skmacy#include <contrib/rdma/iw_cm.h> 64178784Skmacy 65178784Skmacyenum iw_cm_state { 66178784Skmacy IW_CM_STATE_IDLE, /* unbound, inactive */ 67178784Skmacy IW_CM_STATE_LISTEN, /* listen waiting for connect */ 68178784Skmacy IW_CM_STATE_CONN_RECV, /* inbound waiting for user accept */ 69178784Skmacy IW_CM_STATE_CONN_SENT, /* outbound waiting for peer accept */ 70178784Skmacy IW_CM_STATE_ESTABLISHED, /* established */ 71178784Skmacy IW_CM_STATE_CLOSING, /* disconnect */ 72178784Skmacy IW_CM_STATE_DESTROYING /* object being deleted */ 73178784Skmacy}; 74178784Skmacy 75178784Skmacystruct iwcm_id_private { 76178784Skmacy struct iw_cm_id id; 77178784Skmacy enum iw_cm_state state; 78178784Skmacy unsigned long flags; 79178784Skmacy struct ib_qp *qp; 80178784Skmacy void * destroy_comp; 81178784Skmacy void * connect_wait; 82178784Skmacy TAILQ_HEAD(, iwcm_work) work_list; 83178784Skmacy struct mtx lock; 84178784Skmacy volatile int refcount; 85178784Skmacy TAILQ_HEAD(, iwcm_work) work_free_list; 86178784Skmacy}; 87178784Skmacy 88178784Skmacy#define IWCM_F_CALLBACK_DESTROY 1 89178784Skmacy#define IWCM_F_CONNECT_WAIT 2 90178784Skmacy 91178784Skmacystatic struct taskqueue *iwcm_wq; 92178784Skmacystruct iwcm_work { 93178784Skmacy struct task task; 94178784Skmacy struct iwcm_id_private *cm_id; 95178784Skmacy TAILQ_ENTRY(iwcm_work) list; 96178784Skmacy struct iw_cm_event event; 97178784Skmacy TAILQ_ENTRY(iwcm_work) free_list; 98178784Skmacy}; 99178784Skmacy 100178784Skmacy/* 101178784Skmacy * The following services provide a mechanism for pre-allocating iwcm_work 102178784Skmacy * elements. The design pre-allocates them based on the cm_id type: 103178784Skmacy * LISTENING IDS: Get enough elements preallocated to handle the 104178784Skmacy * listen backlog. 105178784Skmacy * ACTIVE IDS: 4: CONNECT_REPLY, ESTABLISHED, DISCONNECT, CLOSE 106178784Skmacy * PASSIVE IDS: 3: ESTABLISHED, DISCONNECT, CLOSE 107178784Skmacy * 108178784Skmacy * Allocating them in connect and listen avoids having to deal 109178784Skmacy * with allocation failures on the event upcall from the provider (which 110178784Skmacy * is called in the interrupt context). 111178784Skmacy * 112178784Skmacy * One exception is when creating the cm_id for incoming connection requests. 113178784Skmacy * There are two cases: 114178784Skmacy * 1) in the event upcall, cm_event_handler(), for a listening cm_id. If 115178784Skmacy * the backlog is exceeded, then no more connection request events will 116178784Skmacy * be processed. cm_event_handler() returns ENOMEM in this case. Its up 117178784Skmacy * to the provider to reject the connection request. 118178784Skmacy * 2) in the connection request workqueue handler, cm_conn_req_handler(). 119178784Skmacy * If work elements cannot be allocated for the new connect request cm_id, 120178784Skmacy * then IWCM will call the provider reject method. This is ok since 121178784Skmacy * cm_conn_req_handler() runs in the workqueue thread context. 122178784Skmacy */ 123178784Skmacy 124178784Skmacystatic struct iwcm_work *get_work(struct iwcm_id_private *cm_id_priv) 125178784Skmacy{ 126178784Skmacy struct iwcm_work *work; 127178784Skmacy 128178784Skmacy if (TAILQ_EMPTY(&cm_id_priv->work_free_list)) 129178784Skmacy return NULL; 130178784Skmacy work = TAILQ_FIRST(&cm_id_priv->work_free_list); 131178784Skmacy TAILQ_REMOVE(&cm_id_priv->work_free_list, work, free_list); 132178784Skmacy return work; 133178784Skmacy} 134178784Skmacy 135178784Skmacystatic void put_work(struct iwcm_work *work) 136178784Skmacy{ 137178784Skmacy TAILQ_INSERT_HEAD(&work->cm_id->work_free_list, work, free_list); 138178784Skmacy} 139178784Skmacy 140178784Skmacystatic void dealloc_work_entries(struct iwcm_id_private *cm_id_priv) 141178784Skmacy{ 142178784Skmacy struct iwcm_work *e, *tmp; 143178784Skmacy 144178784Skmacy TAILQ_FOREACH_SAFE(e, &cm_id_priv->work_free_list, free_list, tmp) 145178784Skmacy free(e, M_DEVBUF); 146178784Skmacy} 147178784Skmacy 148178784Skmacystatic int alloc_work_entries(struct iwcm_id_private *cm_id_priv, int count) 149178784Skmacy{ 150178784Skmacy struct iwcm_work *work; 151178784Skmacy 152178784Skmacy PANIC_IF(!TAILQ_EMPTY(&cm_id_priv->work_free_list)); 153178784Skmacy while (count--) { 154178784Skmacy work = malloc(sizeof(struct iwcm_work), M_DEVBUF, M_NOWAIT); 155178784Skmacy if (!work) { 156178784Skmacy dealloc_work_entries(cm_id_priv); 157178784Skmacy return (ENOMEM); 158178784Skmacy } 159178784Skmacy work->cm_id = cm_id_priv; 160178784Skmacy put_work(work); 161178784Skmacy } 162178784Skmacy return 0; 163178784Skmacy} 164178784Skmacy 165178784Skmacy/* 166178784Skmacy * Save private data from incoming connection requests to 167178784Skmacy * iw_cm_event, so the low level driver doesn't have to. Adjust 168178784Skmacy * the event ptr to point to the local copy. 169178784Skmacy */ 170178784Skmacystatic int copy_private_data(struct iw_cm_event *event) 171178784Skmacy{ 172178784Skmacy void *p; 173178784Skmacy 174178784Skmacy p = malloc(event->private_data_len, M_DEVBUF, M_NOWAIT); 175178784Skmacy if (!p) 176178784Skmacy return (ENOMEM); 177178784Skmacy bcopy(event->private_data, p, event->private_data_len); 178178784Skmacy event->private_data = p; 179178784Skmacy return 0; 180178784Skmacy} 181178784Skmacy 182178784Skmacystatic void free_cm_id(struct iwcm_id_private *cm_id_priv) 183178784Skmacy{ 184178784Skmacy dealloc_work_entries(cm_id_priv); 185178784Skmacy free(cm_id_priv, M_DEVBUF); 186178784Skmacy} 187178784Skmacy 188178784Skmacy/* 189178784Skmacy * Release a reference on cm_id. If the last reference is being 190178784Skmacy * released, enable the waiting thread (in iw_destroy_cm_id) to 191178784Skmacy * get woken up, and return 1 if a thread is already waiting. 192178784Skmacy */ 193178784Skmacystatic int iwcm_deref_id(struct iwcm_id_private *cm_id_priv) 194178784Skmacy{ 195178784Skmacy mtx_lock(&cm_id_priv->lock); 196178784Skmacy PANIC_IF(atomic_load_acq_int(&cm_id_priv->refcount)==0); 197178784Skmacy if (atomic_fetchadd_int(&cm_id_priv->refcount, -1) == 1) { 198178784Skmacy PANIC_IF(!TAILQ_EMPTY(&cm_id_priv->work_list)); 199178784Skmacy wakeup(&cm_id_priv->destroy_comp); 200178784Skmacy mtx_unlock(&cm_id_priv->lock); 201178784Skmacy return 1; 202178784Skmacy } 203178784Skmacy mtx_unlock(&cm_id_priv->lock); 204178784Skmacy 205178784Skmacy return 0; 206178784Skmacy} 207178784Skmacy 208178784Skmacystatic void add_ref(struct iw_cm_id *cm_id) 209178784Skmacy{ 210178784Skmacy struct iwcm_id_private *cm_id_priv; 211178784Skmacy cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); 212178784Skmacy mtx_lock(&cm_id_priv->lock); 213178784Skmacy atomic_add_int(&cm_id_priv->refcount, 1); 214178784Skmacy mtx_unlock(&cm_id_priv->lock); 215178784Skmacy} 216178784Skmacy 217178784Skmacystatic void rem_ref(struct iw_cm_id *cm_id) 218178784Skmacy{ 219178784Skmacy struct iwcm_id_private *cm_id_priv; 220178784Skmacy cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); 221178784Skmacy if (iwcm_deref_id(cm_id_priv) && 222178784Skmacy isset(&cm_id_priv->flags, IWCM_F_CALLBACK_DESTROY)) { 223178784Skmacy PANIC_IF(!TAILQ_EMPTY(&cm_id_priv->work_list)); 224178784Skmacy free_cm_id(cm_id_priv); 225178784Skmacy } 226178784Skmacy} 227178784Skmacy 228178784Skmacystatic int cm_event_handler(struct iw_cm_id *cm_id, struct iw_cm_event *event); 229178784Skmacy 230178784Skmacystruct iw_cm_id *iw_create_cm_id(struct ib_device *device, 231178784Skmacy struct socket *so, 232178784Skmacy iw_cm_handler cm_handler, 233178784Skmacy void *context) 234178784Skmacy{ 235178784Skmacy struct iwcm_id_private *cm_id_priv; 236178784Skmacy 237178784Skmacy KASSERT(so, ("iw_create_cm_id called with NULL socket!")); 238178784Skmacy cm_id_priv = malloc(sizeof(*cm_id_priv), M_DEVBUF, M_NOWAIT); 239178784Skmacy if (!cm_id_priv) 240178784Skmacy return ERR_PTR(ENOMEM); 241178784Skmacy bzero(cm_id_priv, sizeof *cm_id_priv); 242178784Skmacy 243178784Skmacy cm_id_priv->state = IW_CM_STATE_IDLE; 244178784Skmacy cm_id_priv->id.device = device; 245178784Skmacy cm_id_priv->id.cm_handler = cm_handler; 246178784Skmacy cm_id_priv->id.context = context; 247178784Skmacy cm_id_priv->id.event_handler = cm_event_handler; 248178784Skmacy cm_id_priv->id.add_ref = add_ref; 249178784Skmacy cm_id_priv->id.rem_ref = rem_ref; 250178784Skmacy cm_id_priv->id.so = so; 251178784Skmacy mtx_init(&cm_id_priv->lock, "cm_id_priv", NULL, MTX_DUPOK|MTX_DEF); 252178784Skmacy atomic_store_rel_int(&cm_id_priv->refcount, 1); 253178784Skmacy TAILQ_INIT(&cm_id_priv->work_list); 254178784Skmacy TAILQ_INIT(&cm_id_priv->work_free_list); 255178784Skmacy 256178784Skmacy return &cm_id_priv->id; 257178784Skmacy} 258178784Skmacy 259178784Skmacy 260178784Skmacystatic int iwcm_modify_qp_err(struct ib_qp *qp) 261178784Skmacy{ 262178784Skmacy struct ib_qp_attr qp_attr; 263178784Skmacy 264178784Skmacy if (!qp) 265178784Skmacy return (EINVAL); 266178784Skmacy 267178784Skmacy qp_attr.qp_state = IB_QPS_ERR; 268178784Skmacy return ib_modify_qp(qp, &qp_attr, IB_QP_STATE); 269178784Skmacy} 270178784Skmacy 271178784Skmacy/* 272178784Skmacy * This is really the RDMAC CLOSING state. It is most similar to the 273178784Skmacy * IB SQD QP state. 274178784Skmacy */ 275178784Skmacystatic int iwcm_modify_qp_sqd(struct ib_qp *qp) 276178784Skmacy{ 277178784Skmacy struct ib_qp_attr qp_attr; 278178784Skmacy 279178784Skmacy PANIC_IF(qp == NULL); 280178784Skmacy qp_attr.qp_state = IB_QPS_SQD; 281178784Skmacy return ib_modify_qp(qp, &qp_attr, IB_QP_STATE); 282178784Skmacy} 283178784Skmacy 284178784Skmacy/* 285178784Skmacy * CM_ID <-- CLOSING 286178784Skmacy * 287178784Skmacy * Block if a passive or active connection is currently being processed. Then 288178784Skmacy * process the event as follows: 289178784Skmacy * - If we are ESTABLISHED, move to CLOSING and modify the QP state 290178784Skmacy * based on the abrupt flag 291178784Skmacy * - If the connection is already in the CLOSING or IDLE state, the peer is 292178784Skmacy * disconnecting concurrently with us and we've already seen the 293178784Skmacy * DISCONNECT event -- ignore the request and return 0 294178784Skmacy * - Disconnect on a listening endpoint returns EINVAL 295178784Skmacy */ 296178784Skmacyint iw_cm_disconnect(struct iw_cm_id *cm_id, int abrupt) 297178784Skmacy{ 298178784Skmacy struct iwcm_id_private *cm_id_priv; 299178784Skmacy int ret = 0; 300178784Skmacy struct ib_qp *qp = NULL; 301178784Skmacy 302178784Skmacy cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); 303178784Skmacy /* Wait if we're currently in a connect or accept downcall */ 304178784Skmacy mtx_lock(&cm_id_priv->lock); 305178784Skmacy if (isset(&cm_id_priv->flags, IWCM_F_CONNECT_WAIT)) 306178784Skmacy msleep(&cm_id_priv->connect_wait, &cm_id_priv->lock, 0, "iwcm connect1", 0); 307178784Skmacy 308178784Skmacy switch (cm_id_priv->state) { 309178784Skmacy case IW_CM_STATE_ESTABLISHED: 310178784Skmacy cm_id_priv->state = IW_CM_STATE_CLOSING; 311178784Skmacy 312178784Skmacy /* QP could be <nul> for user-mode client */ 313178784Skmacy if (cm_id_priv->qp) 314178784Skmacy qp = cm_id_priv->qp; 315178784Skmacy else 316178784Skmacy ret = EINVAL; 317178784Skmacy break; 318178784Skmacy case IW_CM_STATE_LISTEN: 319178784Skmacy ret = EINVAL; 320178784Skmacy break; 321178784Skmacy case IW_CM_STATE_CLOSING: 322178784Skmacy /* remote peer closed first */ 323178784Skmacy case IW_CM_STATE_IDLE: 324178784Skmacy /* accept or connect returned !0 */ 325178784Skmacy break; 326178784Skmacy case IW_CM_STATE_CONN_RECV: 327178784Skmacy /* 328178784Skmacy * App called disconnect before/without calling accept after 329178784Skmacy * connect_request event delivered. 330178784Skmacy */ 331178784Skmacy break; 332178784Skmacy case IW_CM_STATE_CONN_SENT: 333178784Skmacy /* Can only get here if wait above fails */ 334178784Skmacy default: 335178784Skmacy panic("just cuz"); 336178784Skmacy } 337178784Skmacy mtx_unlock(&cm_id_priv->lock); 338178784Skmacy 339178784Skmacy if (qp) { 340178784Skmacy if (abrupt) 341178784Skmacy ret = iwcm_modify_qp_err(qp); 342178784Skmacy else 343178784Skmacy ret = iwcm_modify_qp_sqd(qp); 344178784Skmacy 345178784Skmacy /* 346178784Skmacy * If both sides are disconnecting the QP could 347178784Skmacy * already be in ERR or SQD states 348178784Skmacy */ 349178784Skmacy ret = 0; 350178784Skmacy } 351178784Skmacy 352178784Skmacy return ret; 353178784Skmacy} 354178784Skmacy 355178784Skmacy/* 356178784Skmacy * CM_ID <-- DESTROYING 357178784Skmacy * 358178784Skmacy * Clean up all resources associated with the connection and release 359178784Skmacy * the initial reference taken by iw_create_cm_id. 360178784Skmacy */ 361178784Skmacystatic void destroy_cm_id(struct iw_cm_id *cm_id) 362178784Skmacy{ 363178784Skmacy struct iwcm_id_private *cm_id_priv; 364178784Skmacy int ret; 365178784Skmacy 366178784Skmacy cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); 367178784Skmacy /* 368178784Skmacy * Wait if we're currently in a connect or accept downcall. A 369178784Skmacy * listening endpoint should never block here. 370178784Skmacy */ 371178784Skmacy mtx_lock(&cm_id_priv->lock); 372178784Skmacy if (isset(&cm_id_priv->flags, IWCM_F_CONNECT_WAIT)) 373178784Skmacy msleep(&cm_id_priv->connect_wait, &cm_id_priv->lock, 0, "iwcm connect2", 0); 374178784Skmacy 375178784Skmacy switch (cm_id_priv->state) { 376178784Skmacy case IW_CM_STATE_LISTEN: 377178784Skmacy cm_id_priv->state = IW_CM_STATE_DESTROYING; 378178784Skmacy mtx_unlock(&cm_id_priv->lock); 379178784Skmacy /* destroy the listening endpoint */ 380178784Skmacy ret = cm_id->device->iwcm->destroy_listen(cm_id); 381178784Skmacy mtx_lock(&cm_id_priv->lock); 382178784Skmacy break; 383178784Skmacy case IW_CM_STATE_ESTABLISHED: 384178784Skmacy cm_id_priv->state = IW_CM_STATE_DESTROYING; 385178784Skmacy mtx_unlock(&cm_id_priv->lock); 386178784Skmacy /* Abrupt close of the connection */ 387178784Skmacy (void)iwcm_modify_qp_err(cm_id_priv->qp); 388178784Skmacy mtx_lock(&cm_id_priv->lock); 389178784Skmacy break; 390178784Skmacy case IW_CM_STATE_IDLE: 391178784Skmacy case IW_CM_STATE_CLOSING: 392178784Skmacy cm_id_priv->state = IW_CM_STATE_DESTROYING; 393178784Skmacy break; 394178784Skmacy case IW_CM_STATE_CONN_RECV: 395178784Skmacy /* 396178784Skmacy * App called destroy before/without calling accept after 397178784Skmacy * receiving connection request event notification or 398178784Skmacy * returned non zero from the event callback function. 399178784Skmacy * In either case, must tell the provider to reject. 400178784Skmacy */ 401178784Skmacy cm_id_priv->state = IW_CM_STATE_DESTROYING; 402178784Skmacy break; 403178784Skmacy case IW_CM_STATE_CONN_SENT: 404178784Skmacy case IW_CM_STATE_DESTROYING: 405178784Skmacy default: 406178784Skmacy panic("just cuz"); 407178784Skmacy break; 408178784Skmacy } 409178784Skmacy if (cm_id_priv->qp) { 410178784Skmacy cm_id_priv->id.device->iwcm->rem_ref(cm_id_priv->qp); 411178784Skmacy cm_id_priv->qp = NULL; 412178784Skmacy } 413178784Skmacy mtx_unlock(&cm_id_priv->lock); 414178784Skmacy 415178784Skmacy (void)iwcm_deref_id(cm_id_priv); 416178784Skmacy} 417178784Skmacy 418178784Skmacy/* 419178784Skmacy * This function is only called by the application thread and cannot 420178784Skmacy * be called by the event thread. The function will wait for all 421178784Skmacy * references to be released on the cm_id and then free the cm_id 422178784Skmacy * object. 423178784Skmacy */ 424178784Skmacyvoid iw_destroy_cm_id(struct iw_cm_id *cm_id) 425178784Skmacy{ 426178784Skmacy struct iwcm_id_private *cm_id_priv; 427178784Skmacy 428178784Skmacy cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); 429178784Skmacy PANIC_IF(isset(&cm_id_priv->flags, IWCM_F_CALLBACK_DESTROY)); 430178784Skmacy 431178784Skmacy destroy_cm_id(cm_id); 432178784Skmacy 433178784Skmacy mtx_lock(&cm_id_priv->lock); 434178784Skmacy if (atomic_load_acq_int(&cm_id_priv->refcount)) 435178784Skmacy msleep(&cm_id_priv->destroy_comp, &cm_id_priv->lock, 0, "iwcm destroy", 0); 436178784Skmacy mtx_unlock(&cm_id_priv->lock); 437178784Skmacy 438178784Skmacy free_cm_id(cm_id_priv); 439178784Skmacy} 440178784Skmacy 441178784Skmacy/* 442178784Skmacy * CM_ID <-- LISTEN 443178784Skmacy * 444178784Skmacy * Start listening for connect requests. Generates one CONNECT_REQUEST 445178784Skmacy * event for each inbound connect request. 446178784Skmacy */ 447178784Skmacyint iw_cm_listen(struct iw_cm_id *cm_id, int backlog) 448178784Skmacy{ 449178784Skmacy struct iwcm_id_private *cm_id_priv; 450178784Skmacy int ret; 451178784Skmacy 452178784Skmacy cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); 453178784Skmacy 454178784Skmacy ret = alloc_work_entries(cm_id_priv, backlog); 455178784Skmacy if (ret) 456178784Skmacy return ret; 457178784Skmacy 458178784Skmacy mtx_lock(&cm_id_priv->lock); 459178784Skmacy switch (cm_id_priv->state) { 460178784Skmacy case IW_CM_STATE_IDLE: 461178784Skmacy cm_id_priv->state = IW_CM_STATE_LISTEN; 462178784Skmacy mtx_unlock(&cm_id_priv->lock); 463178784Skmacy ret = cm_id->device->iwcm->create_listen(cm_id, backlog); 464178784Skmacy if (ret) 465178784Skmacy cm_id_priv->state = IW_CM_STATE_IDLE; 466178784Skmacy mtx_lock(&cm_id_priv->lock); 467178784Skmacy break; 468178784Skmacy default: 469178784Skmacy ret = EINVAL; 470178784Skmacy } 471178784Skmacy mtx_unlock(&cm_id_priv->lock); 472178784Skmacy 473178784Skmacy return ret; 474178784Skmacy} 475178784Skmacy 476178784Skmacy/* 477178784Skmacy * CM_ID <-- IDLE 478178784Skmacy * 479178784Skmacy * Rejects an inbound connection request. No events are generated. 480178784Skmacy */ 481178784Skmacyint iw_cm_reject(struct iw_cm_id *cm_id, 482178784Skmacy const void *private_data, 483178784Skmacy u8 private_data_len) 484178784Skmacy{ 485178784Skmacy struct iwcm_id_private *cm_id_priv; 486178784Skmacy int ret; 487178784Skmacy 488178784Skmacy cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); 489178784Skmacy setbit(&cm_id_priv->flags, IWCM_F_CONNECT_WAIT); 490178784Skmacy 491178784Skmacy mtx_lock(&cm_id_priv->lock); 492178784Skmacy if (cm_id_priv->state != IW_CM_STATE_CONN_RECV) { 493178784Skmacy clrbit(&cm_id_priv->flags, IWCM_F_CONNECT_WAIT); 494178784Skmacy wakeup(&cm_id_priv->connect_wait); 495178784Skmacy mtx_unlock(&cm_id_priv->lock); 496178784Skmacy return (EINVAL); 497178784Skmacy } 498178784Skmacy cm_id_priv->state = IW_CM_STATE_IDLE; 499178784Skmacy mtx_unlock(&cm_id_priv->lock); 500178784Skmacy 501178784Skmacy ret = cm_id->device->iwcm->reject(cm_id, private_data, 502178784Skmacy private_data_len); 503178784Skmacy 504178784Skmacy mtx_lock(&cm_id_priv->lock); 505178784Skmacy clrbit(&cm_id_priv->flags, IWCM_F_CONNECT_WAIT); 506178784Skmacy wakeup(&cm_id_priv->connect_wait); 507178784Skmacy mtx_unlock(&cm_id_priv->lock); 508178784Skmacy 509178784Skmacy return ret; 510178784Skmacy} 511178784Skmacy 512178784Skmacy/* 513178784Skmacy * CM_ID <-- ESTABLISHED 514178784Skmacy * 515178784Skmacy * Accepts an inbound connection request and generates an ESTABLISHED 516178784Skmacy * event. Callers of iw_cm_disconnect and iw_destroy_cm_id will block 517178784Skmacy * until the ESTABLISHED event is received from the provider. 518178784Skmacy */ 519178784Skmacyint iw_cm_accept(struct iw_cm_id *cm_id, 520178784Skmacy struct iw_cm_conn_param *iw_param) 521178784Skmacy{ 522178784Skmacy struct iwcm_id_private *cm_id_priv; 523178784Skmacy struct ib_qp *qp; 524178784Skmacy int ret; 525178784Skmacy 526178784Skmacy cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); 527178784Skmacy setbit(&cm_id_priv->flags, IWCM_F_CONNECT_WAIT); 528178784Skmacy 529178784Skmacy mtx_lock(&cm_id_priv->lock); 530178784Skmacy if (cm_id_priv->state != IW_CM_STATE_CONN_RECV) { 531178784Skmacy clrbit(&cm_id_priv->flags, IWCM_F_CONNECT_WAIT); 532178784Skmacy wakeup(&cm_id_priv->connect_wait); 533178784Skmacy mtx_unlock(&cm_id_priv->lock); 534178784Skmacy 535178784Skmacy return (EINVAL); 536178784Skmacy } 537178784Skmacy /* Get the ib_qp given the QPN */ 538178784Skmacy qp = cm_id->device->iwcm->get_qp(cm_id->device, iw_param->qpn); 539178784Skmacy if (!qp) { 540178784Skmacy mtx_unlock(&cm_id_priv->lock); 541178784Skmacy return (EINVAL); 542178784Skmacy } 543178784Skmacy cm_id->device->iwcm->add_ref(qp); 544178784Skmacy cm_id_priv->qp = qp; 545178784Skmacy mtx_unlock(&cm_id_priv->lock); 546178784Skmacy 547178784Skmacy ret = cm_id->device->iwcm->accept(cm_id, iw_param); 548178784Skmacy if (ret) { 549178784Skmacy /* An error on accept precludes provider events */ 550178784Skmacy PANIC_IF(cm_id_priv->state != IW_CM_STATE_CONN_RECV); 551178784Skmacy cm_id_priv->state = IW_CM_STATE_IDLE; 552178784Skmacy mtx_lock(&cm_id_priv->lock); 553178784Skmacy if (cm_id_priv->qp) { 554178784Skmacy cm_id->device->iwcm->rem_ref(qp); 555178784Skmacy cm_id_priv->qp = NULL; 556178784Skmacy } 557178784Skmacy clrbit(&cm_id_priv->flags, IWCM_F_CONNECT_WAIT); 558178784Skmacy wakeup(&cm_id_priv->connect_wait); 559178784Skmacy mtx_unlock(&cm_id_priv->lock); 560178784Skmacy } 561178784Skmacy 562178784Skmacy return ret; 563178784Skmacy} 564178784Skmacy 565178784Skmacy/* 566178784Skmacy * Active Side: CM_ID <-- CONN_SENT 567178784Skmacy * 568178784Skmacy * If successful, results in the generation of a CONNECT_REPLY 569178784Skmacy * event. iw_cm_disconnect and iw_cm_destroy will block until the 570178784Skmacy * CONNECT_REPLY event is received from the provider. 571178784Skmacy */ 572178784Skmacyint iw_cm_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *iw_param) 573178784Skmacy{ 574178784Skmacy struct iwcm_id_private *cm_id_priv; 575178784Skmacy int ret; 576178784Skmacy struct ib_qp *qp; 577178784Skmacy 578178784Skmacy cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); 579178784Skmacy 580178784Skmacy ret = alloc_work_entries(cm_id_priv, 4); 581178784Skmacy if (ret) 582178784Skmacy return ret; 583178784Skmacy 584178784Skmacy setbit(&cm_id_priv->flags, IWCM_F_CONNECT_WAIT); 585178784Skmacy mtx_lock(&cm_id_priv->lock); 586178784Skmacy 587178784Skmacy if (cm_id_priv->state != IW_CM_STATE_IDLE) { 588178784Skmacy clrbit(&cm_id_priv->flags, IWCM_F_CONNECT_WAIT); 589178784Skmacy wakeup(&cm_id_priv->connect_wait); 590178784Skmacy mtx_unlock(&cm_id_priv->lock); 591178784Skmacy 592178784Skmacy return (EINVAL); 593178784Skmacy } 594178784Skmacy 595178784Skmacy /* Get the ib_qp given the QPN */ 596178784Skmacy qp = cm_id->device->iwcm->get_qp(cm_id->device, iw_param->qpn); 597178784Skmacy if (!qp) { 598178784Skmacy mtx_unlock(&cm_id_priv->lock); 599178784Skmacy return (EINVAL); 600178784Skmacy } 601178784Skmacy cm_id->device->iwcm->add_ref(qp); 602178784Skmacy cm_id_priv->qp = qp; 603178784Skmacy cm_id_priv->state = IW_CM_STATE_CONN_SENT; 604178784Skmacy mtx_unlock(&cm_id_priv->lock); 605178784Skmacy 606178784Skmacy ret = cm_id->device->iwcm->connect(cm_id, iw_param); 607178784Skmacy if (ret) { 608178784Skmacy mtx_lock(&cm_id_priv->lock); 609178784Skmacy if (cm_id_priv->qp) { 610178784Skmacy cm_id->device->iwcm->rem_ref(qp); 611178784Skmacy cm_id_priv->qp = NULL; 612178784Skmacy } 613178784Skmacy PANIC_IF(cm_id_priv->state != IW_CM_STATE_CONN_SENT); 614178784Skmacy cm_id_priv->state = IW_CM_STATE_IDLE; 615178784Skmacy clrbit(&cm_id_priv->flags, IWCM_F_CONNECT_WAIT); 616178784Skmacy wakeup(&cm_id_priv->connect_wait); 617178784Skmacy mtx_unlock(&cm_id_priv->lock); 618178784Skmacy 619178784Skmacy } 620178784Skmacy 621178784Skmacy return ret; 622178784Skmacy} 623178784Skmacy 624178784Skmacy/* 625178784Skmacy * Passive Side: new CM_ID <-- CONN_RECV 626178784Skmacy * 627178784Skmacy * Handles an inbound connect request. The function creates a new 628178784Skmacy * iw_cm_id to represent the new connection and inherits the client 629178784Skmacy * callback function and other attributes from the listening parent. 630178784Skmacy * 631178784Skmacy * The work item contains a pointer to the listen_cm_id and the event. The 632178784Skmacy * listen_cm_id contains the client cm_handler, context and 633178784Skmacy * device. These are copied when the device is cloned. The event 634178784Skmacy * contains the new four tuple. 635178784Skmacy * 636178784Skmacy * An error on the child should not affect the parent, so this 637178784Skmacy * function does not return a value. 638178784Skmacy */ 639178784Skmacystatic void cm_conn_req_handler(struct iwcm_id_private *listen_id_priv, 640178784Skmacy struct iw_cm_event *iw_event) 641178784Skmacy{ 642178784Skmacy struct iw_cm_id *cm_id; 643178784Skmacy struct iwcm_id_private *cm_id_priv; 644178784Skmacy int ret; 645178784Skmacy 646178784Skmacy /* 647178784Skmacy * The provider should never generate a connection request 648178784Skmacy * event with a bad status. 649178784Skmacy */ 650178784Skmacy PANIC_IF(iw_event->status); 651178784Skmacy 652178784Skmacy /* 653178784Skmacy * We could be destroying the listening id. If so, ignore this 654178784Skmacy * upcall. 655178784Skmacy */ 656178784Skmacy mtx_lock(&listen_id_priv->lock); 657178784Skmacy if (listen_id_priv->state != IW_CM_STATE_LISTEN) { 658178784Skmacy mtx_unlock(&listen_id_priv->lock); 659178784Skmacy goto out; 660178784Skmacy } 661178784Skmacy mtx_unlock(&listen_id_priv->lock); 662178784Skmacy 663178784Skmacy cm_id = iw_create_cm_id(listen_id_priv->id.device, 664178784Skmacy iw_event->so, 665178784Skmacy listen_id_priv->id.cm_handler, 666178784Skmacy listen_id_priv->id.context); 667178784Skmacy /* If the cm_id could not be created, ignore the request */ 668178784Skmacy if (IS_ERR(cm_id)) 669178784Skmacy goto out; 670178784Skmacy 671178784Skmacy cm_id->provider_data = iw_event->provider_data; 672178784Skmacy cm_id->local_addr = iw_event->local_addr; 673178784Skmacy cm_id->remote_addr = iw_event->remote_addr; 674178784Skmacy 675178784Skmacy cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); 676178784Skmacy cm_id_priv->state = IW_CM_STATE_CONN_RECV; 677178784Skmacy 678178784Skmacy ret = alloc_work_entries(cm_id_priv, 3); 679178784Skmacy if (ret) { 680178784Skmacy iw_cm_reject(cm_id, NULL, 0); 681178784Skmacy iw_destroy_cm_id(cm_id); 682178784Skmacy goto out; 683178784Skmacy } 684178784Skmacy 685178784Skmacy /* Call the client CM handler */ 686178784Skmacy ret = cm_id->cm_handler(cm_id, iw_event); 687178784Skmacy if (ret) { 688178784Skmacy iw_cm_reject(cm_id, NULL, 0); 689178784Skmacy setbit(&cm_id_priv->flags, IWCM_F_CALLBACK_DESTROY); 690178784Skmacy 691178784Skmacy destroy_cm_id(cm_id); 692178784Skmacy if (atomic_load_acq_int(&cm_id_priv->refcount)==0) 693178784Skmacy free_cm_id(cm_id_priv); 694178784Skmacy } 695178784Skmacy 696178784Skmacyout: 697178784Skmacy if (iw_event->private_data_len) 698178784Skmacy free(iw_event->private_data, M_DEVBUF); 699178784Skmacy} 700178784Skmacy 701178784Skmacy/* 702178784Skmacy * Passive Side: CM_ID <-- ESTABLISHED 703178784Skmacy * 704178784Skmacy * The provider generated an ESTABLISHED event which means that 705178784Skmacy * the MPA negotion has completed successfully and we are now in MPA 706178784Skmacy * FPDU mode. 707178784Skmacy * 708178784Skmacy * This event can only be received in the CONN_RECV state. If the 709178784Skmacy * remote peer closed, the ESTABLISHED event would be received followed 710178784Skmacy * by the CLOSE event. If the app closes, it will block until we wake 711178784Skmacy * it up after processing this event. 712178784Skmacy */ 713178784Skmacystatic int cm_conn_est_handler(struct iwcm_id_private *cm_id_priv, 714178784Skmacy struct iw_cm_event *iw_event) 715178784Skmacy{ 716178784Skmacy int ret; 717178784Skmacy 718178784Skmacy mtx_lock(&cm_id_priv->lock); 719178784Skmacy 720178784Skmacy /* 721178784Skmacy * We clear the CONNECT_WAIT bit here to allow the callback 722178784Skmacy * function to call iw_cm_disconnect. Calling iw_destroy_cm_id 723178784Skmacy * from a callback handler is not allowed. 724178784Skmacy */ 725178784Skmacy clrbit(&cm_id_priv->flags, IWCM_F_CONNECT_WAIT); 726178784Skmacy PANIC_IF(cm_id_priv->state != IW_CM_STATE_CONN_RECV); 727178784Skmacy cm_id_priv->state = IW_CM_STATE_ESTABLISHED; 728178784Skmacy ret = cm_id_priv->id.cm_handler(&cm_id_priv->id, iw_event); 729178784Skmacy wakeup(&cm_id_priv->connect_wait); 730178784Skmacy mtx_unlock(&cm_id_priv->lock); 731178784Skmacy 732178784Skmacy return ret; 733178784Skmacy} 734178784Skmacy 735178784Skmacy/* 736178784Skmacy * Active Side: CM_ID <-- ESTABLISHED 737178784Skmacy * 738178784Skmacy * The app has called connect and is waiting for the established event to 739178784Skmacy * post it's requests to the server. This event will wake up anyone 740178784Skmacy * blocked in iw_cm_disconnect or iw_destroy_id. 741178784Skmacy */ 742178784Skmacystatic int cm_conn_rep_handler(struct iwcm_id_private *cm_id_priv, 743178784Skmacy struct iw_cm_event *iw_event) 744178784Skmacy{ 745178784Skmacy int ret; 746178784Skmacy 747178784Skmacy mtx_lock(&cm_id_priv->lock); 748178784Skmacy /* 749178784Skmacy * Clear the connect wait bit so a callback function calling 750178784Skmacy * iw_cm_disconnect will not wait and deadlock this thread 751178784Skmacy */ 752178784Skmacy clrbit(&cm_id_priv->flags, IWCM_F_CONNECT_WAIT); 753178784Skmacy PANIC_IF(cm_id_priv->state != IW_CM_STATE_CONN_SENT); 754178784Skmacy if (iw_event->status == IW_CM_EVENT_STATUS_ACCEPTED) { 755178784Skmacy cm_id_priv->id.local_addr = iw_event->local_addr; 756178784Skmacy cm_id_priv->id.remote_addr = iw_event->remote_addr; 757178784Skmacy cm_id_priv->state = IW_CM_STATE_ESTABLISHED; 758178784Skmacy } else { 759178784Skmacy /* REJECTED or RESET */ 760178784Skmacy cm_id_priv->id.device->iwcm->rem_ref(cm_id_priv->qp); 761178784Skmacy cm_id_priv->qp = NULL; 762178784Skmacy cm_id_priv->state = IW_CM_STATE_IDLE; 763178784Skmacy } 764178784Skmacy mtx_unlock(&cm_id_priv->lock); 765178784Skmacy ret = cm_id_priv->id.cm_handler(&cm_id_priv->id, iw_event); 766178784Skmacy 767178784Skmacy mtx_lock(&cm_id_priv->lock); 768178784Skmacy if (iw_event->private_data_len) 769178784Skmacy free(iw_event->private_data, M_DEVBUF); 770178784Skmacy 771178784Skmacy /* Wake up waiters on connect complete */ 772178784Skmacy wakeup(&cm_id_priv->connect_wait); 773178784Skmacy mtx_unlock(&cm_id_priv->lock); 774178784Skmacy 775178784Skmacy return ret; 776178784Skmacy} 777178784Skmacy 778178784Skmacy/* 779178784Skmacy * CM_ID <-- CLOSING 780178784Skmacy * 781178784Skmacy * If in the ESTABLISHED state, move to CLOSING. 782178784Skmacy */ 783178784Skmacystatic void cm_disconnect_handler(struct iwcm_id_private *cm_id_priv, 784178784Skmacy struct iw_cm_event *iw_event) 785178784Skmacy{ 786178784Skmacy 787178784Skmacy mtx_lock(&cm_id_priv->lock); 788178784Skmacy if (cm_id_priv->state == IW_CM_STATE_ESTABLISHED) 789178784Skmacy cm_id_priv->state = IW_CM_STATE_CLOSING; 790178784Skmacy mtx_unlock(&cm_id_priv->lock); 791178784Skmacy} 792178784Skmacy 793178784Skmacy/* 794178784Skmacy * CM_ID <-- IDLE 795178784Skmacy * 796178784Skmacy * If in the ESTBLISHED or CLOSING states, the QP will have have been 797178784Skmacy * moved by the provider to the ERR state. Disassociate the CM_ID from 798178784Skmacy * the QP, move to IDLE, and remove the 'connected' reference. 799178784Skmacy * 800178784Skmacy * If in some other state, the cm_id was destroyed asynchronously. 801178784Skmacy * This is the last reference that will result in waking up 802178784Skmacy * the app thread blocked in iw_destroy_cm_id. 803178784Skmacy */ 804178784Skmacystatic int cm_close_handler(struct iwcm_id_private *cm_id_priv, 805178784Skmacy struct iw_cm_event *iw_event) 806178784Skmacy{ 807178784Skmacy int ret = 0; 808178784Skmacy mtx_lock(&cm_id_priv->lock); 809178784Skmacy 810178784Skmacy if (cm_id_priv->qp) { 811178784Skmacy cm_id_priv->id.device->iwcm->rem_ref(cm_id_priv->qp); 812178784Skmacy cm_id_priv->qp = NULL; 813178784Skmacy } 814178784Skmacy switch (cm_id_priv->state) { 815178784Skmacy case IW_CM_STATE_ESTABLISHED: 816178784Skmacy case IW_CM_STATE_CLOSING: 817178784Skmacy cm_id_priv->state = IW_CM_STATE_IDLE; 818178784Skmacy mtx_unlock(&cm_id_priv->lock); 819178784Skmacy ret = cm_id_priv->id.cm_handler(&cm_id_priv->id, iw_event); 820178784Skmacy mtx_lock(&cm_id_priv->lock); 821178784Skmacy break; 822178784Skmacy case IW_CM_STATE_DESTROYING: 823178784Skmacy break; 824178784Skmacy default: 825178784Skmacy panic("just cuz"); 826178784Skmacy } 827178784Skmacy mtx_unlock(&cm_id_priv->lock); 828178784Skmacy 829178784Skmacy return ret; 830178784Skmacy} 831178784Skmacy 832178784Skmacystatic int process_event(struct iwcm_id_private *cm_id_priv, 833178784Skmacy struct iw_cm_event *iw_event) 834178784Skmacy{ 835178784Skmacy int ret = 0; 836178784Skmacy 837178784Skmacy switch (iw_event->event) { 838178784Skmacy case IW_CM_EVENT_CONNECT_REQUEST: 839178784Skmacy cm_conn_req_handler(cm_id_priv, iw_event); 840178784Skmacy break; 841178784Skmacy case IW_CM_EVENT_CONNECT_REPLY: 842178784Skmacy ret = cm_conn_rep_handler(cm_id_priv, iw_event); 843178784Skmacy break; 844178784Skmacy case IW_CM_EVENT_ESTABLISHED: 845178784Skmacy ret = cm_conn_est_handler(cm_id_priv, iw_event); 846178784Skmacy break; 847178784Skmacy case IW_CM_EVENT_DISCONNECT: 848178784Skmacy cm_disconnect_handler(cm_id_priv, iw_event); 849178784Skmacy break; 850178784Skmacy case IW_CM_EVENT_CLOSE: 851178784Skmacy ret = cm_close_handler(cm_id_priv, iw_event); 852178784Skmacy break; 853178784Skmacy default: 854178784Skmacy panic("just cuz"); 855178784Skmacy } 856178784Skmacy 857178784Skmacy return ret; 858178784Skmacy} 859178784Skmacy 860178784Skmacy/* 861178784Skmacy * Process events on the work_list for the cm_id. If the callback 862178784Skmacy * function requests that the cm_id be deleted, a flag is set in the 863178784Skmacy * cm_id flags to indicate that when the last reference is 864178784Skmacy * removed, the cm_id is to be destroyed. This is necessary to 865178784Skmacy * distinguish between an object that will be destroyed by the app 866178784Skmacy * thread asleep on the destroy_comp list vs. an object destroyed 867178784Skmacy * here synchronously when the last reference is removed. 868178784Skmacy */ 869178784Skmacystatic void cm_work_handler(void *context, int pending) 870178784Skmacy{ 871178784Skmacy struct iwcm_work *work = context; 872178784Skmacy struct iw_cm_event levent; 873178784Skmacy struct iwcm_id_private *cm_id_priv = work->cm_id; 874178784Skmacy int empty; 875178784Skmacy int ret = 0; 876178784Skmacy 877178784Skmacy mtx_lock(&cm_id_priv->lock); 878178784Skmacy empty = TAILQ_EMPTY(&cm_id_priv->work_list); 879178784Skmacy while (!empty) { 880178784Skmacy work = TAILQ_FIRST(&cm_id_priv->work_list); 881178784Skmacy TAILQ_REMOVE(&cm_id_priv->work_list, work, list); 882178784Skmacy empty = TAILQ_EMPTY(&cm_id_priv->work_list); 883178784Skmacy levent = work->event; 884178784Skmacy put_work(work); 885178784Skmacy mtx_unlock(&cm_id_priv->lock); 886178784Skmacy 887178784Skmacy ret = process_event(cm_id_priv, &levent); 888178784Skmacy if (ret) { 889178784Skmacy setbit(&cm_id_priv->flags, IWCM_F_CALLBACK_DESTROY); 890178784Skmacy destroy_cm_id(&cm_id_priv->id); 891178784Skmacy } 892178784Skmacy PANIC_IF(atomic_load_acq_int(&cm_id_priv->refcount)==0); 893178784Skmacy if (iwcm_deref_id(cm_id_priv)) { 894178784Skmacy if (isset(&cm_id_priv->flags, 895178784Skmacy IWCM_F_CALLBACK_DESTROY)) { 896178784Skmacy PANIC_IF(!TAILQ_EMPTY(&cm_id_priv->work_list)); 897178784Skmacy free_cm_id(cm_id_priv); 898178784Skmacy } 899178784Skmacy return; 900178784Skmacy } 901178784Skmacy mtx_lock(&cm_id_priv->lock); 902178784Skmacy } 903178784Skmacy mtx_unlock(&cm_id_priv->lock); 904178784Skmacy} 905178784Skmacy 906178784Skmacy/* 907178784Skmacy * This function is called on interrupt context. Schedule events on 908178784Skmacy * the iwcm_wq thread to allow callback functions to downcall into 909178784Skmacy * the CM and/or block. Events are queued to a per-CM_ID 910178784Skmacy * work_list. If this is the first event on the work_list, the work 911178784Skmacy * element is also queued on the iwcm_wq thread. 912178784Skmacy * 913178784Skmacy * Each event holds a reference on the cm_id. Until the last posted 914178784Skmacy * event has been delivered and processed, the cm_id cannot be 915178784Skmacy * deleted. 916178784Skmacy * 917178784Skmacy * Returns: 918178784Skmacy * 0 - the event was handled. 919178784Skmacy * ENOMEM - the event was not handled due to lack of resources. 920178784Skmacy */ 921178784Skmacystatic int cm_event_handler(struct iw_cm_id *cm_id, 922178784Skmacy struct iw_cm_event *iw_event) 923178784Skmacy{ 924178784Skmacy struct iwcm_work *work; 925178784Skmacy struct iwcm_id_private *cm_id_priv; 926178784Skmacy int ret = 0; 927178784Skmacy 928178784Skmacy cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); 929178784Skmacy 930178784Skmacy mtx_lock(&cm_id_priv->lock); 931178784Skmacy work = get_work(cm_id_priv); 932178784Skmacy if (!work) { 933178784Skmacy ret = ENOMEM; 934178784Skmacy goto out; 935178784Skmacy } 936178784Skmacy 937178784Skmacy TASK_INIT(&work->task, 0, cm_work_handler, work); 938178784Skmacy work->cm_id = cm_id_priv; 939178784Skmacy work->event = *iw_event; 940178784Skmacy 941178784Skmacy if ((work->event.event == IW_CM_EVENT_CONNECT_REQUEST || 942178784Skmacy work->event.event == IW_CM_EVENT_CONNECT_REPLY) && 943178784Skmacy work->event.private_data_len) { 944178784Skmacy ret = copy_private_data(&work->event); 945178784Skmacy if (ret) { 946178784Skmacy put_work(work); 947178784Skmacy goto out; 948178784Skmacy } 949178784Skmacy } 950178784Skmacy 951178784Skmacy atomic_add_acq_int(&cm_id_priv->refcount, 1); 952178784Skmacy if (TAILQ_EMPTY(&cm_id_priv->work_list)) { 953178784Skmacy TAILQ_INSERT_TAIL(&cm_id_priv->work_list, work, list); 954178784Skmacy taskqueue_enqueue(iwcm_wq, &work->task); 955178784Skmacy } else 956178784Skmacy TAILQ_INSERT_TAIL(&cm_id_priv->work_list, work, list); 957178784Skmacyout: 958178784Skmacy mtx_unlock(&cm_id_priv->lock); 959178784Skmacy return ret; 960178784Skmacy} 961178784Skmacy 962178784Skmacystatic int iwcm_init_qp_init_attr(struct iwcm_id_private *cm_id_priv, 963178784Skmacy struct ib_qp_attr *qp_attr, 964178784Skmacy int *qp_attr_mask) 965178784Skmacy{ 966178784Skmacy int ret; 967178784Skmacy 968178784Skmacy mtx_lock(&cm_id_priv->lock); 969178784Skmacy switch (cm_id_priv->state) { 970178784Skmacy case IW_CM_STATE_IDLE: 971178784Skmacy case IW_CM_STATE_CONN_SENT: 972178784Skmacy case IW_CM_STATE_CONN_RECV: 973178784Skmacy case IW_CM_STATE_ESTABLISHED: 974178784Skmacy *qp_attr_mask = IB_QP_STATE | IB_QP_ACCESS_FLAGS; 975178784Skmacy qp_attr->qp_access_flags = IB_ACCESS_LOCAL_WRITE | 976178784Skmacy IB_ACCESS_REMOTE_WRITE| 977178784Skmacy IB_ACCESS_REMOTE_READ; 978178784Skmacy ret = 0; 979178784Skmacy break; 980178784Skmacy default: 981178784Skmacy ret = EINVAL; 982178784Skmacy break; 983178784Skmacy } 984178784Skmacy mtx_unlock(&cm_id_priv->lock); 985178784Skmacy return ret; 986178784Skmacy} 987178784Skmacy 988178784Skmacystatic int iwcm_init_qp_rts_attr(struct iwcm_id_private *cm_id_priv, 989178784Skmacy struct ib_qp_attr *qp_attr, 990178784Skmacy int *qp_attr_mask) 991178784Skmacy{ 992178784Skmacy int ret; 993178784Skmacy 994178784Skmacy mtx_lock(&cm_id_priv->lock); 995178784Skmacy switch (cm_id_priv->state) { 996178784Skmacy case IW_CM_STATE_IDLE: 997178784Skmacy case IW_CM_STATE_CONN_SENT: 998178784Skmacy case IW_CM_STATE_CONN_RECV: 999178784Skmacy case IW_CM_STATE_ESTABLISHED: 1000178784Skmacy *qp_attr_mask = 0; 1001178784Skmacy ret = 0; 1002178784Skmacy break; 1003178784Skmacy default: 1004178784Skmacy ret = EINVAL; 1005178784Skmacy break; 1006178784Skmacy } 1007178784Skmacy mtx_unlock(&cm_id_priv->lock); 1008178784Skmacy return ret; 1009178784Skmacy} 1010178784Skmacy 1011178784Skmacyint iw_cm_init_qp_attr(struct iw_cm_id *cm_id, 1012178784Skmacy struct ib_qp_attr *qp_attr, 1013178784Skmacy int *qp_attr_mask) 1014178784Skmacy{ 1015178784Skmacy struct iwcm_id_private *cm_id_priv; 1016178784Skmacy int ret; 1017178784Skmacy 1018178784Skmacy cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); 1019178784Skmacy switch (qp_attr->qp_state) { 1020178784Skmacy case IB_QPS_INIT: 1021178784Skmacy case IB_QPS_RTR: 1022178784Skmacy ret = iwcm_init_qp_init_attr(cm_id_priv, 1023178784Skmacy qp_attr, qp_attr_mask); 1024178784Skmacy break; 1025178784Skmacy case IB_QPS_RTS: 1026178784Skmacy ret = iwcm_init_qp_rts_attr(cm_id_priv, 1027178784Skmacy qp_attr, qp_attr_mask); 1028178784Skmacy break; 1029178784Skmacy default: 1030178784Skmacy ret = EINVAL; 1031178784Skmacy break; 1032178784Skmacy } 1033178784Skmacy return ret; 1034178784Skmacy} 1035178784Skmacy 1036178784Skmacystatic int iw_cm_init(void) 1037178784Skmacy{ 1038178784Skmacy iwcm_wq = taskqueue_create("iw_cm_wq", M_NOWAIT, taskqueue_thread_enqueue, &iwcm_wq); 1039178784Skmacy if (!iwcm_wq) 1040178784Skmacy return (ENOMEM); 1041178784Skmacy 1042178784Skmacy taskqueue_start_threads(&iwcm_wq, 1, PI_NET, "iw_cm_wq thread"); 1043178784Skmacy return 0; 1044178784Skmacy} 1045178784Skmacy 1046178784Skmacystatic void iw_cm_cleanup(void) 1047178784Skmacy{ 1048178784Skmacy taskqueue_free(iwcm_wq); 1049178784Skmacy} 1050178784Skmacy 1051178784Skmacystatic int 1052178784Skmacyiw_cm_load(module_t mod, int cmd, void *arg) 1053178784Skmacy{ 1054178784Skmacy int err = 0; 1055178784Skmacy 1056178784Skmacy switch (cmd) { 1057178784Skmacy case MOD_LOAD: 1058178784Skmacy printf("Loading rdma_iwcm.\n"); 1059178784Skmacy 1060178784Skmacy iw_cm_init(); 1061178784Skmacy break; 1062178784Skmacy case MOD_QUIESCE: 1063178784Skmacy break; 1064178784Skmacy case MOD_UNLOAD: 1065178784Skmacy printf("Unloading rdma_iwcm.\n"); 1066178784Skmacy iw_cm_cleanup(); 1067178784Skmacy break; 1068178784Skmacy case MOD_SHUTDOWN: 1069178784Skmacy break; 1070178784Skmacy default: 1071178784Skmacy err = EOPNOTSUPP; 1072178784Skmacy break; 1073178784Skmacy } 1074178784Skmacy 1075178784Skmacy return (err); 1076178784Skmacy} 1077178784Skmacy 1078178784Skmacystatic moduledata_t mod_data = { 1079178784Skmacy "rdma_iwcm", 1080178784Skmacy iw_cm_load, 1081178784Skmacy 0 1082178784Skmacy}; 1083178784Skmacy 1084178784SkmacyMODULE_VERSION(rdma_iwcm, 1); 1085178784SkmacyMODULE_DEPEND(rdma_iwcm, rdma_core, 1, 1, 1); 1086178784SkmacyDECLARE_MODULE(rdma_iwcm, mod_data, SI_SUB_EXEC, SI_ORDER_ANY); 1087