Path: blob/main/sys/ofed/drivers/infiniband/ulp/sdp/sdp_cma.c
39566 views
/*-1* SPDX-License-Identifier: BSD-2-Clause OR GPL-2.02*3* Copyright (c) 2006 Mellanox Technologies Ltd. All rights reserved.4*5* This software is available to you under a choice of one of two6* licenses. You may choose to be licensed under the terms of the GNU7* General Public License (GPL) Version 2, available from the file8* COPYING in the main directory of this source tree, or the9* OpenIB.org BSD license below:10*11* Redistribution and use in source and binary forms, with or12* without modification, are permitted provided that the following13* conditions are met:14*15* - Redistributions of source code must retain the above16* copyright notice, this list of conditions and the following17* disclaimer.18*19* - Redistributions in binary form must reproduce the above20* copyright notice, this list of conditions and the following21* disclaimer in the documentation and/or other materials22* provided with the distribution.23*24* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,25* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF26* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND27* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS28* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN29* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN30* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE31* SOFTWARE.32*33* $Id$34*/35#include "sdp.h"3637#define SDP_MAJV_MINV 0x223839SDP_MODPARAM_SINT(sdp_link_layer_ib_only, 1, "Support only link layer of "40"type Infiniband");4142enum {43SDP_HH_SIZE = 76,44SDP_HAH_SIZE = 180,45};4647static void48sdp_qp_event_handler(struct ib_event *event, void *data)49{50struct socket *sk = data;5152sdp_dbg(sk, "QP Event: %s (%d)", ib_event_msg(event->event),53event->event);54}5556static int57sdp_get_max_dev_sge(struct ib_device *dev)58{59struct ib_device_attr *device_attr;60static int max_sges = -1;6162if (max_sges > 0)63goto out;6465device_attr = &dev->attrs;66max_sges = device_attr->max_sge;6768out:69return max_sges;70}7172static int73sdp_init_qp(struct socket *sk, struct rdma_cm_id *id)74{75struct ib_qp_init_attr qp_init_attr = {76.event_handler = sdp_qp_event_handler,77.qp_context = sk,78.cap.max_send_wr = SDP_TX_SIZE,79.cap.max_recv_wr = SDP_RX_SIZE,80.sq_sig_type = IB_SIGNAL_REQ_WR,81.qp_type = IB_QPT_RC,82};83struct ib_device *device = id->device;84struct sdp_sock *ssk;85int rc;8687sdp_dbg(sk, "%s\n", __func__);8889ssk = sdp_sk(sk);90ssk->max_sge = sdp_get_max_dev_sge(device);91sdp_dbg(sk, "Max sges: %d\n", ssk->max_sge);9293qp_init_attr.cap.max_send_sge = MIN(ssk->max_sge, SDP_MAX_SEND_SGES);94sdp_dbg(sk, "Setting max send sge to: %d\n",95qp_init_attr.cap.max_send_sge);9697qp_init_attr.cap.max_recv_sge = MIN(ssk->max_sge, SDP_MAX_RECV_SGES);98sdp_dbg(sk, "Setting max recv sge to: %d\n",99qp_init_attr.cap.max_recv_sge);100101ssk->sdp_dev = ib_get_client_data(device, &sdp_client);102if (!ssk->sdp_dev) {103sdp_warn(sk, "SDP not available on device %s\n", device->name);104rc = -ENODEV;105goto err_rx;106}107108rc = sdp_rx_ring_create(ssk, device);109if (rc)110goto err_rx;111112rc = sdp_tx_ring_create(ssk, device);113if (rc)114goto err_tx;115116qp_init_attr.recv_cq = ssk->rx_ring.cq;117qp_init_attr.send_cq = ssk->tx_ring.cq;118119rc = rdma_create_qp(id, ssk->sdp_dev->pd, &qp_init_attr);120if (rc) {121sdp_warn(sk, "Unable to create QP: %d.\n", rc);122goto err_qp;123}124ssk->qp = id->qp;125ssk->ib_device = device;126ssk->qp_active = 1;127ssk->context.device = device;128129sdp_dbg(sk, "%s done\n", __func__);130return 0;131132err_qp:133sdp_tx_ring_destroy(ssk);134err_tx:135sdp_rx_ring_destroy(ssk);136err_rx:137return rc;138}139140static int141sdp_connect_handler(struct socket *sk, struct rdma_cm_id *id,142struct rdma_cm_event *event)143{144struct sockaddr_in *src_addr;145struct sockaddr_in *dst_addr;146struct socket *child;147const struct sdp_hh *h;148struct sdp_sock *ssk;149int rc;150151sdp_dbg(sk, "%s %p -> %p\n", __func__, sdp_sk(sk)->id, id);152153h = event->param.conn.private_data;154SDP_DUMP_PACKET(sk, "RX", NULL, &h->bsdh);155156if (!h->max_adverts)157return -EINVAL;158159child = sonewconn(sk, SS_ISCONNECTED);160if (!child)161return -ENOMEM;162163ssk = sdp_sk(child);164rc = sdp_init_qp(child, id);165if (rc)166return rc;167SDP_WLOCK(ssk);168id->context = ssk;169ssk->id = id;170ssk->socket = child;171ssk->cred = crhold(child->so_cred);172dst_addr = (struct sockaddr_in *)&id->route.addr.dst_addr;173src_addr = (struct sockaddr_in *)&id->route.addr.src_addr;174ssk->fport = dst_addr->sin_port;175ssk->faddr = dst_addr->sin_addr.s_addr;176ssk->lport = src_addr->sin_port;177ssk->max_bufs = ntohs(h->bsdh.bufs);178atomic_set(&ssk->tx_ring.credits, ssk->max_bufs);179ssk->min_bufs = tx_credits(ssk) / 4;180ssk->xmit_size_goal = ntohl(h->localrcvsz) - sizeof(struct sdp_bsdh);181sdp_init_buffers(ssk, rcvbuf_initial_size);182ssk->state = TCPS_SYN_RECEIVED;183SDP_WUNLOCK(ssk);184185return 0;186}187188static int189sdp_response_handler(struct socket *sk, struct rdma_cm_id *id,190struct rdma_cm_event *event)191{192const struct sdp_hah *h;193struct sockaddr_in *dst_addr;194struct sdp_sock *ssk;195sdp_dbg(sk, "%s\n", __func__);196197ssk = sdp_sk(sk);198SDP_WLOCK(ssk);199ssk->state = TCPS_ESTABLISHED;200sdp_set_default_moderation(ssk);201if (ssk->flags & SDP_DROPPED) {202SDP_WUNLOCK(ssk);203return 0;204}205if (sk->so_options & SO_KEEPALIVE)206sdp_start_keepalive_timer(sk);207h = event->param.conn.private_data;208SDP_DUMP_PACKET(sk, "RX", NULL, &h->bsdh);209ssk->max_bufs = ntohs(h->bsdh.bufs);210atomic_set(&ssk->tx_ring.credits, ssk->max_bufs);211ssk->min_bufs = tx_credits(ssk) / 4;212ssk->xmit_size_goal =213ntohl(h->actrcvsz) - sizeof(struct sdp_bsdh);214ssk->poll_cq = 1;215216dst_addr = (struct sockaddr_in *)&id->route.addr.dst_addr;217ssk->fport = dst_addr->sin_port;218ssk->faddr = dst_addr->sin_addr.s_addr;219soisconnected(sk);220SDP_WUNLOCK(ssk);221222return 0;223}224225static int226sdp_connected_handler(struct socket *sk, struct rdma_cm_event *event)227{228struct sdp_sock *ssk;229230sdp_dbg(sk, "%s\n", __func__);231232ssk = sdp_sk(sk);233SDP_WLOCK(ssk);234ssk->state = TCPS_ESTABLISHED;235236sdp_set_default_moderation(ssk);237238if (sk->so_options & SO_KEEPALIVE)239sdp_start_keepalive_timer(sk);240241if ((ssk->flags & SDP_DROPPED) == 0)242soisconnected(sk);243SDP_WUNLOCK(ssk);244return 0;245}246247static int248sdp_disconnected_handler(struct socket *sk)249{250struct sdp_sock *ssk;251252ssk = sdp_sk(sk);253sdp_dbg(sk, "%s\n", __func__);254255SDP_WLOCK_ASSERT(ssk);256if (sdp_sk(sk)->state == TCPS_SYN_RECEIVED) {257sdp_connected_handler(sk, NULL);258259if (rcv_nxt(ssk))260return 0;261}262263return -ECONNRESET;264}265266int267sdp_cma_handler(struct rdma_cm_id *id, struct rdma_cm_event *event)268{269struct rdma_conn_param conn_param;270struct socket *sk;271struct sdp_sock *ssk;272struct sdp_hah hah;273struct sdp_hh hh;274275int rc = 0;276277ssk = id->context;278sk = NULL;279if (ssk)280sk = ssk->socket;281if (!ssk || !sk || !ssk->id) {282sdp_dbg(sk,283"cm_id is being torn down, event %d, ssk %p, sk %p, id %p\n",284event->event, ssk, sk, id);285return event->event == RDMA_CM_EVENT_CONNECT_REQUEST ?286-EINVAL : 0;287}288289sdp_dbg(sk, "%s event %d id %p\n", __func__, event->event, id);290switch (event->event) {291case RDMA_CM_EVENT_ADDR_RESOLVED:292sdp_dbg(sk, "RDMA_CM_EVENT_ADDR_RESOLVED\n");293294if (sdp_link_layer_ib_only &&295rdma_node_get_transport(id->device->node_type) ==296RDMA_TRANSPORT_IB &&297rdma_port_get_link_layer(id->device, id->port_num) !=298IB_LINK_LAYER_INFINIBAND) {299sdp_dbg(sk, "Link layer is: %d. Only IB link layer "300"is allowed\n",301rdma_port_get_link_layer(id->device, id->port_num));302rc = -ENETUNREACH;303break;304}305306rc = rdma_resolve_route(id, SDP_ROUTE_TIMEOUT);307break;308case RDMA_CM_EVENT_ADDR_ERROR:309sdp_dbg(sk, "RDMA_CM_EVENT_ADDR_ERROR\n");310rc = -ENETUNREACH;311break;312case RDMA_CM_EVENT_ROUTE_RESOLVED:313sdp_dbg(sk, "RDMA_CM_EVENT_ROUTE_RESOLVED : %p\n", id);314rc = sdp_init_qp(sk, id);315if (rc)316break;317atomic_set(&sdp_sk(sk)->remote_credits,318rx_ring_posted(sdp_sk(sk)));319memset(&hh, 0, sizeof hh);320hh.bsdh.mid = SDP_MID_HELLO;321hh.bsdh.len = htonl(sizeof(struct sdp_hh));322hh.max_adverts = 1;323hh.ipv_cap = 0x40;324hh.majv_minv = SDP_MAJV_MINV;325sdp_init_buffers(sdp_sk(sk), rcvbuf_initial_size);326hh.bsdh.bufs = htons(rx_ring_posted(sdp_sk(sk)));327hh.localrcvsz = hh.desremrcvsz = htonl(sdp_sk(sk)->recv_bytes);328hh.max_adverts = 0x1;329sdp_sk(sk)->laddr =330((struct sockaddr_in *)&id->route.addr.src_addr)->sin_addr.s_addr;331memset(&conn_param, 0, sizeof conn_param);332conn_param.private_data_len = sizeof hh;333conn_param.private_data = &hh;334conn_param.responder_resources = 4 /* TODO */;335conn_param.initiator_depth = 4 /* TODO */;336conn_param.retry_count = SDP_RETRY_COUNT;337SDP_DUMP_PACKET(NULL, "TX", NULL, &hh.bsdh);338rc = rdma_connect(id, &conn_param);339break;340case RDMA_CM_EVENT_ROUTE_ERROR:341sdp_dbg(sk, "RDMA_CM_EVENT_ROUTE_ERROR : %p\n", id);342rc = -ETIMEDOUT;343break;344case RDMA_CM_EVENT_CONNECT_REQUEST:345sdp_dbg(sk, "RDMA_CM_EVENT_CONNECT_REQUEST\n");346rc = sdp_connect_handler(sk, id, event);347if (rc) {348sdp_dbg(sk, "Destroying qp\n");349rdma_reject(id, NULL, 0);350break;351}352ssk = id->context;353atomic_set(&ssk->remote_credits, rx_ring_posted(ssk));354memset(&hah, 0, sizeof hah);355hah.bsdh.mid = SDP_MID_HELLO_ACK;356hah.bsdh.bufs = htons(rx_ring_posted(ssk));357hah.bsdh.len = htonl(sizeof(struct sdp_hah));358hah.majv_minv = SDP_MAJV_MINV;359hah.ext_max_adverts = 1; /* Doesn't seem to be mandated by spec,360but just in case */361hah.actrcvsz = htonl(ssk->recv_bytes);362memset(&conn_param, 0, sizeof conn_param);363conn_param.private_data_len = sizeof hah;364conn_param.private_data = &hah;365conn_param.responder_resources = 4 /* TODO */;366conn_param.initiator_depth = 4 /* TODO */;367conn_param.retry_count = SDP_RETRY_COUNT;368SDP_DUMP_PACKET(sk, "TX", NULL, &hah.bsdh);369rc = rdma_accept(id, &conn_param);370if (rc) {371ssk->id = NULL;372id->qp = NULL;373id->context = NULL;374}375break;376case RDMA_CM_EVENT_CONNECT_RESPONSE:377sdp_dbg(sk, "RDMA_CM_EVENT_CONNECT_RESPONSE\n");378rc = sdp_response_handler(sk, id, event);379if (rc) {380sdp_dbg(sk, "Destroying qp\n");381rdma_reject(id, NULL, 0);382} else383rc = rdma_accept(id, NULL);384break;385case RDMA_CM_EVENT_CONNECT_ERROR:386sdp_dbg(sk, "RDMA_CM_EVENT_CONNECT_ERROR\n");387rc = -ETIMEDOUT;388break;389case RDMA_CM_EVENT_UNREACHABLE:390sdp_dbg(sk, "RDMA_CM_EVENT_UNREACHABLE\n");391rc = -ENETUNREACH;392break;393case RDMA_CM_EVENT_REJECTED:394sdp_dbg(sk, "RDMA_CM_EVENT_REJECTED\n");395rc = -ECONNREFUSED;396break;397case RDMA_CM_EVENT_ESTABLISHED:398sdp_dbg(sk, "RDMA_CM_EVENT_ESTABLISHED\n");399sdp_sk(sk)->laddr =400((struct sockaddr_in *)&id->route.addr.src_addr)->sin_addr.s_addr;401rc = sdp_connected_handler(sk, event);402break;403case RDMA_CM_EVENT_DISCONNECTED: /* This means DREQ/DREP received */404sdp_dbg(sk, "RDMA_CM_EVENT_DISCONNECTED\n");405406SDP_WLOCK(ssk);407if (ssk->state == TCPS_LAST_ACK) {408sdp_cancel_dreq_wait_timeout(ssk);409410sdp_dbg(sk, "%s: waiting for Infiniband tear down\n",411__func__);412}413ssk->qp_active = 0;414SDP_WUNLOCK(ssk);415rdma_disconnect(id);416SDP_WLOCK(ssk);417if (ssk->state != TCPS_TIME_WAIT) {418if (ssk->state == TCPS_CLOSE_WAIT) {419sdp_dbg(sk, "IB teardown while in "420"TCPS_CLOSE_WAIT taking reference to "421"let close() finish the work\n");422}423rc = sdp_disconnected_handler(sk);424if (rc)425rc = -EPIPE;426}427SDP_WUNLOCK(ssk);428break;429case RDMA_CM_EVENT_TIMEWAIT_EXIT:430sdp_dbg(sk, "RDMA_CM_EVENT_TIMEWAIT_EXIT\n");431SDP_WLOCK(ssk);432rc = sdp_disconnected_handler(sk);433SDP_WUNLOCK(ssk);434break;435case RDMA_CM_EVENT_DEVICE_REMOVAL:436sdp_dbg(sk, "RDMA_CM_EVENT_DEVICE_REMOVAL\n");437rc = -ENETRESET;438break;439default:440printk(KERN_ERR "SDP: Unexpected CMA event: %d\n",441event->event);442rc = -ECONNABORTED;443break;444}445446sdp_dbg(sk, "event %s (%d) done. status %d\n",447rdma_event_msg(event->event), event->event, rc);448449if (rc) {450SDP_WLOCK(ssk);451if (ssk->id == id) {452ssk->id = NULL;453id->qp = NULL;454id->context = NULL;455if (sdp_notify(ssk, -rc))456SDP_WUNLOCK(ssk);457} else458SDP_WUNLOCK(ssk);459}460461return rc;462}463464465