Path: blob/main/sys/ofed/drivers/infiniband/ulp/sdp/sdp_rx.c
39566 views
/*-1* SPDX-License-Identifier: BSD-2-Clause OR GPL-2.02*3* Copyright (c) 2009 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#include "sdp.h"3435SDP_MODPARAM_INT(rcvbuf_initial_size, 32 * 1024,36"Receive buffer initial size in bytes.");37SDP_MODPARAM_SINT(rcvbuf_scale, 0x8,38"Receive buffer size scale factor.");3940/* Like tcp_fin - called when SDP_MID_DISCONNECT is received */41static void42sdp_handle_disconn(struct sdp_sock *ssk)43{4445sdp_dbg(ssk->socket, "%s\n", __func__);4647SDP_WLOCK_ASSERT(ssk);48if (TCPS_HAVERCVDFIN(ssk->state) == 0)49socantrcvmore(ssk->socket);5051switch (ssk->state) {52case TCPS_SYN_RECEIVED:53case TCPS_ESTABLISHED:54ssk->state = TCPS_CLOSE_WAIT;55break;5657case TCPS_FIN_WAIT_1:58/* Received a reply FIN - start Infiniband tear down */59sdp_dbg(ssk->socket,60"%s: Starting Infiniband tear down sending DREQ\n",61__func__);6263sdp_cancel_dreq_wait_timeout(ssk);64ssk->qp_active = 0;65if (ssk->id) {66struct rdma_cm_id *id;6768id = ssk->id;69SDP_WUNLOCK(ssk);70rdma_disconnect(id);71SDP_WLOCK(ssk);72} else {73sdp_warn(ssk->socket,74"%s: ssk->id is NULL\n", __func__);75return;76}77break;78case TCPS_TIME_WAIT:79/* This is a mutual close situation and we've got the DREQ from80the peer before the SDP_MID_DISCONNECT */81break;82case TCPS_CLOSED:83/* FIN arrived after IB teardown started - do nothing */84sdp_dbg(ssk->socket, "%s: fin in state %s\n",85__func__, sdp_state_str(ssk->state));86return;87default:88sdp_warn(ssk->socket,89"%s: FIN in unexpected state. state=%d\n",90__func__, ssk->state);91break;92}93}9495static int96sdp_post_recv(struct sdp_sock *ssk)97{98struct sdp_buf *rx_req;99int i, rc;100u64 addr;101struct ib_device *dev;102struct ib_recv_wr rx_wr = { NULL };103struct ib_sge ibsge[SDP_MAX_RECV_SGES];104struct ib_sge *sge = ibsge;105const struct ib_recv_wr *bad_wr;106struct mbuf *mb, *m;107struct sdp_bsdh *h;108int id = ring_head(ssk->rx_ring);109110/* Now, allocate and repost recv */111sdp_prf(ssk->socket, mb, "Posting mb");112mb = m_getm2(NULL, ssk->recv_bytes, M_NOWAIT, MT_DATA, M_PKTHDR);113if (mb == NULL) {114/* Retry so we can't stall out with no memory. */115if (!rx_ring_posted(ssk))116queue_work(rx_comp_wq, &ssk->rx_comp_work);117return -1;118}119for (m = mb; m != NULL; m = m->m_next) {120m->m_len = M_SIZE(m);121mb->m_pkthdr.len += m->m_len;122}123h = mtod(mb, struct sdp_bsdh *);124rx_req = ssk->rx_ring.buffer + (id & (SDP_RX_SIZE - 1));125rx_req->mb = mb;126dev = ssk->ib_device;127for (i = 0; mb != NULL; i++, mb = mb->m_next, sge++) {128addr = ib_dma_map_single(dev, mb->m_data, mb->m_len,129DMA_TO_DEVICE);130/* TODO: proper error handling */131BUG_ON(ib_dma_mapping_error(dev, addr));132BUG_ON(i >= SDP_MAX_RECV_SGES);133rx_req->mapping[i] = addr;134sge->addr = addr;135sge->length = mb->m_len;136sge->lkey = ssk->sdp_dev->pd->local_dma_lkey;137}138139rx_wr.next = NULL;140rx_wr.wr_id = id | SDP_OP_RECV;141rx_wr.sg_list = ibsge;142rx_wr.num_sge = i;143rc = ib_post_recv(ssk->qp, &rx_wr, &bad_wr);144if (unlikely(rc)) {145sdp_warn(ssk->socket, "ib_post_recv failed. status %d\n", rc);146147sdp_cleanup_sdp_buf(ssk, rx_req, DMA_FROM_DEVICE);148m_freem(mb);149150sdp_notify(ssk, ECONNRESET);151152return -1;153}154155atomic_inc(&ssk->rx_ring.head);156SDPSTATS_COUNTER_INC(post_recv);157158return 0;159}160161static inline int162sdp_post_recvs_needed(struct sdp_sock *ssk)163{164unsigned long bytes_in_process;165unsigned long max_bytes;166int buffer_size;167int posted;168169if (!ssk->qp_active || !ssk->socket)170return 0;171172posted = rx_ring_posted(ssk);173if (posted >= SDP_RX_SIZE)174return 0;175if (posted < SDP_MIN_TX_CREDITS)176return 1;177178buffer_size = ssk->recv_bytes;179max_bytes = max(ssk->socket->so_rcv.sb_hiwat,180(1 + SDP_MIN_TX_CREDITS) * buffer_size);181max_bytes *= rcvbuf_scale;182/*183* Compute bytes in the receive queue and socket buffer.184*/185bytes_in_process = (posted - SDP_MIN_TX_CREDITS) * buffer_size;186bytes_in_process += sbused(&ssk->socket->so_rcv);187188return bytes_in_process < max_bytes;189}190191static inline void192sdp_post_recvs(struct sdp_sock *ssk)193{194195while (sdp_post_recvs_needed(ssk))196if (sdp_post_recv(ssk))197return;198}199200static inline struct mbuf *201sdp_sock_queue_rcv_mb(struct socket *sk, struct mbuf *mb)202{203struct sdp_sock *ssk = sdp_sk(sk);204struct sdp_bsdh *h;205206h = mtod(mb, struct sdp_bsdh *);207208#ifdef SDP_ZCOPY209SDP_SKB_CB(mb)->seq = rcv_nxt(ssk);210if (h->mid == SDP_MID_SRCAVAIL) {211struct sdp_srcah *srcah = (struct sdp_srcah *)(h+1);212struct rx_srcavail_state *rx_sa;213214ssk->srcavail_cancel_mseq = 0;215216ssk->rx_sa = rx_sa = RX_SRCAVAIL_STATE(mb) = kzalloc(217sizeof(struct rx_srcavail_state), M_NOWAIT);218219rx_sa->mseq = ntohl(h->mseq);220rx_sa->used = 0;221rx_sa->len = mb_len = ntohl(srcah->len);222rx_sa->rkey = ntohl(srcah->rkey);223rx_sa->vaddr = be64_to_cpu(srcah->vaddr);224rx_sa->flags = 0;225226if (ssk->tx_sa) {227sdp_dbg_data(ssk->socket, "got RX SrcAvail while waiting "228"for TX SrcAvail. waking up TX SrcAvail"229"to be aborted\n");230wake_up(sk->sk_sleep);231}232233atomic_add(mb->len, &ssk->rcv_nxt);234sdp_dbg_data(sk, "queueing SrcAvail. mb_len = %d vaddr = %lld\n",235mb_len, rx_sa->vaddr);236} else237#endif238{239atomic_add(mb->m_pkthdr.len, &ssk->rcv_nxt);240}241242m_adj(mb, SDP_HEAD_SIZE);243SOCKBUF_LOCK(&sk->so_rcv);244if (unlikely(h->flags & SDP_OOB_PRES))245sdp_urg(ssk, mb);246sbappend_locked(&sk->so_rcv, mb, 0);247sorwakeup_locked(sk);248return mb;249}250251static int252sdp_get_recv_bytes(struct sdp_sock *ssk, u32 new_size)253{254255return MIN(new_size, SDP_MAX_PACKET);256}257258int259sdp_init_buffers(struct sdp_sock *ssk, u32 new_size)260{261262ssk->recv_bytes = sdp_get_recv_bytes(ssk, new_size);263sdp_post_recvs(ssk);264265return 0;266}267268int269sdp_resize_buffers(struct sdp_sock *ssk, u32 new_size)270{271u32 curr_size = ssk->recv_bytes;272u32 max_size = SDP_MAX_PACKET;273274if (new_size > curr_size && new_size <= max_size) {275ssk->recv_bytes = sdp_get_recv_bytes(ssk, new_size);276return 0;277}278return -1;279}280281static void282sdp_handle_resize_request(struct sdp_sock *ssk, struct sdp_chrecvbuf *buf)283{284if (sdp_resize_buffers(ssk, ntohl(buf->size)) == 0)285ssk->recv_request_head = ring_head(ssk->rx_ring) + 1;286else287ssk->recv_request_head = ring_tail(ssk->rx_ring);288ssk->recv_request = 1;289}290291static void292sdp_handle_resize_ack(struct sdp_sock *ssk, struct sdp_chrecvbuf *buf)293{294u32 new_size = ntohl(buf->size);295296if (new_size > ssk->xmit_size_goal)297ssk->xmit_size_goal = new_size;298}299300static struct mbuf *301sdp_recv_completion(struct sdp_sock *ssk, int id)302{303struct sdp_buf *rx_req;304struct ib_device *dev;305struct mbuf *mb;306307if (unlikely(id != ring_tail(ssk->rx_ring))) {308printk(KERN_WARNING "Bogus recv completion id %d tail %d\n",309id, ring_tail(ssk->rx_ring));310return NULL;311}312313dev = ssk->ib_device;314rx_req = &ssk->rx_ring.buffer[id & (SDP_RX_SIZE - 1)];315mb = rx_req->mb;316sdp_cleanup_sdp_buf(ssk, rx_req, DMA_FROM_DEVICE);317318atomic_inc(&ssk->rx_ring.tail);319atomic_dec(&ssk->remote_credits);320return mb;321}322323static void324sdp_process_rx_ctl_mb(struct sdp_sock *ssk, struct mbuf *mb)325{326struct sdp_bsdh *h;327struct socket *sk;328329SDP_WLOCK_ASSERT(ssk);330331sk = ssk->socket;332h = mtod(mb, struct sdp_bsdh *);333switch (h->mid) {334case SDP_MID_DATA:335case SDP_MID_SRCAVAIL:336sdp_dbg(sk, "DATA after socket rcv was shutdown\n");337338/* got data in RCV_SHUTDOWN */339if (ssk->state == TCPS_FIN_WAIT_1) {340sdp_dbg(sk, "RX data when state = FIN_WAIT1\n");341sdp_notify(ssk, ECONNRESET);342}343344break;345#ifdef SDP_ZCOPY346case SDP_MID_RDMARDCOMPL:347break;348case SDP_MID_SENDSM:349sdp_handle_sendsm(ssk, ntohl(h->mseq_ack));350break;351case SDP_MID_SRCAVAIL_CANCEL:352sdp_dbg_data(sk, "Handling SrcAvailCancel\n");353sdp_prf(sk, NULL, "Handling SrcAvailCancel");354if (ssk->rx_sa) {355ssk->srcavail_cancel_mseq = ntohl(h->mseq);356ssk->rx_sa->flags |= RX_SA_ABORTED;357ssk->rx_sa = NULL; /* TODO: change it into SDP_MID_DATA and get358the dirty logic from recvmsg */359} else {360sdp_dbg(sk, "Got SrcAvailCancel - "361"but no SrcAvail in process\n");362}363break;364case SDP_MID_SINKAVAIL:365sdp_dbg_data(sk, "Got SinkAvail - not supported: ignored\n");366sdp_prf(sk, NULL, "Got SinkAvail - not supported: ignored");367/* FALLTHROUGH */368#endif369case SDP_MID_ABORT:370sdp_dbg_data(sk, "Handling ABORT\n");371sdp_prf(sk, NULL, "Handling ABORT");372sdp_notify(ssk, ECONNRESET);373break;374case SDP_MID_DISCONN:375sdp_dbg_data(sk, "Handling DISCONN\n");376sdp_prf(sk, NULL, "Handling DISCONN");377sdp_handle_disconn(ssk);378break;379case SDP_MID_CHRCVBUF:380sdp_dbg_data(sk, "Handling RX CHRCVBUF\n");381sdp_handle_resize_request(ssk, (struct sdp_chrecvbuf *)(h+1));382break;383case SDP_MID_CHRCVBUF_ACK:384sdp_dbg_data(sk, "Handling RX CHRCVBUF_ACK\n");385sdp_handle_resize_ack(ssk, (struct sdp_chrecvbuf *)(h+1));386break;387default:388/* TODO: Handle other messages */389sdp_warn(sk, "SDP: FIXME MID %d\n", h->mid);390break;391}392m_freem(mb);393}394395static int396sdp_process_rx_mb(struct sdp_sock *ssk, struct mbuf *mb)397{398struct socket *sk;399struct sdp_bsdh *h;400unsigned long mseq_ack;401int credits_before;402403h = mtod(mb, struct sdp_bsdh *);404sk = ssk->socket;405/*406* If another thread is in so_pcbfree this may be partially torn407* down but no further synchronization is required as the destroying408* thread will wait for receive to shutdown before discarding the409* socket.410*/411if (sk == NULL) {412m_freem(mb);413return 0;414}415416SDPSTATS_HIST_LINEAR(credits_before_update, tx_credits(ssk));417418mseq_ack = ntohl(h->mseq_ack);419credits_before = tx_credits(ssk);420atomic_set(&ssk->tx_ring.credits, mseq_ack - ring_head(ssk->tx_ring) +4211 + ntohs(h->bufs));422if (mseq_ack >= ssk->nagle_last_unacked)423ssk->nagle_last_unacked = 0;424425sdp_prf1(ssk->socket, mb, "RX %s +%d c:%d->%d mseq:%d ack:%d\n",426mid2str(h->mid), ntohs(h->bufs), credits_before,427tx_credits(ssk), ntohl(h->mseq), ntohl(h->mseq_ack));428429if (unlikely(h->mid == SDP_MID_DATA &&430mb->m_pkthdr.len == SDP_HEAD_SIZE)) {431/* Credit update is valid even after RCV_SHUTDOWN */432m_freem(mb);433return 0;434}435436if ((h->mid != SDP_MID_DATA && h->mid != SDP_MID_SRCAVAIL) ||437TCPS_HAVERCVDFIN(ssk->state)) {438sdp_prf(sk, NULL, "Control mb - queing to control queue");439#ifdef SDP_ZCOPY440if (h->mid == SDP_MID_SRCAVAIL_CANCEL) {441sdp_dbg_data(sk, "Got SrcAvailCancel. "442"seq: 0x%d seq_ack: 0x%d\n",443ntohl(h->mseq), ntohl(h->mseq_ack));444ssk->srcavail_cancel_mseq = ntohl(h->mseq);445}446447448if (h->mid == SDP_MID_RDMARDCOMPL) {449struct sdp_rrch *rrch = (struct sdp_rrch *)(h+1);450sdp_dbg_data(sk, "RdmaRdCompl message arrived\n");451sdp_handle_rdma_read_compl(ssk, ntohl(h->mseq_ack),452ntohl(rrch->len));453}454#endif455if (mbufq_enqueue(&ssk->rxctlq, mb) != 0)456m_freem(mb);457return (0);458}459460sdp_prf1(sk, NULL, "queueing %s mb\n", mid2str(h->mid));461mb = sdp_sock_queue_rcv_mb(sk, mb);462463464return 0;465}466467/* called only from irq */468static struct mbuf *469sdp_process_rx_wc(struct sdp_sock *ssk, struct ib_wc *wc)470{471struct mbuf *mb;472struct sdp_bsdh *h;473struct socket *sk = ssk->socket;474int mseq;475476mb = sdp_recv_completion(ssk, wc->wr_id);477if (unlikely(!mb))478return NULL;479480if (unlikely(wc->status)) {481if (ssk->qp_active && sk) {482sdp_dbg(sk, "Recv completion with error. "483"Status %s (%d), vendor: %d\n",484ib_wc_status_msg(wc->status), wc->status,485wc->vendor_err);486sdp_abort(sk);487ssk->qp_active = 0;488}489m_freem(mb);490return NULL;491}492493sdp_dbg_data(sk, "Recv completion. ID %d Length %d\n",494(int)wc->wr_id, wc->byte_len);495if (unlikely(wc->byte_len < sizeof(struct sdp_bsdh))) {496sdp_warn(sk, "SDP BUG! byte_len %d < %zd\n",497wc->byte_len, sizeof(struct sdp_bsdh));498m_freem(mb);499return NULL;500}501/* Use m_adj to trim the tail of data we didn't use. */502m_adj(mb, -(mb->m_pkthdr.len - wc->byte_len));503h = mtod(mb, struct sdp_bsdh *);504505SDP_DUMP_PACKET(ssk->socket, "RX", mb, h);506507ssk->rx_packets++;508ssk->rx_bytes += mb->m_pkthdr.len;509510mseq = ntohl(h->mseq);511atomic_set(&ssk->mseq_ack, mseq);512if (mseq != (int)wc->wr_id)513sdp_warn(sk, "SDP BUG! mseq %d != wrid %d\n",514mseq, (int)wc->wr_id);515516return mb;517}518519/* Wakeup writers if we now have credits. */520static void521sdp_bzcopy_write_space(struct sdp_sock *ssk)522{523struct socket *sk = ssk->socket;524525if (tx_credits(ssk) >= ssk->min_bufs && sk)526sowwakeup(sk);527}528529/* only from interrupt. */530static int531sdp_poll_rx_cq(struct sdp_sock *ssk)532{533struct ib_cq *cq = ssk->rx_ring.cq;534struct ib_wc ibwc[SDP_NUM_WC];535int n, i;536int wc_processed = 0;537struct mbuf *mb;538539do {540n = ib_poll_cq(cq, SDP_NUM_WC, ibwc);541for (i = 0; i < n; ++i) {542struct ib_wc *wc = &ibwc[i];543544BUG_ON(!(wc->wr_id & SDP_OP_RECV));545mb = sdp_process_rx_wc(ssk, wc);546if (!mb)547continue;548549sdp_process_rx_mb(ssk, mb);550wc_processed++;551}552} while (n == SDP_NUM_WC);553554if (wc_processed)555sdp_bzcopy_write_space(ssk);556557return wc_processed;558}559560static void561sdp_rx_comp_work(struct work_struct *work)562{563struct sdp_sock *ssk = container_of(work, struct sdp_sock,564rx_comp_work);565566sdp_prf(ssk->socket, NULL, "%s", __func__);567568SDP_WLOCK(ssk);569if (unlikely(!ssk->qp)) {570sdp_prf(ssk->socket, NULL, "qp was destroyed");571goto out;572}573if (unlikely(!ssk->rx_ring.cq)) {574sdp_prf(ssk->socket, NULL, "rx_ring.cq is NULL");575goto out;576}577578if (unlikely(!ssk->poll_cq)) {579struct rdma_cm_id *id = ssk->id;580if (id && id->qp)581rdma_notify(id, IB_EVENT_COMM_EST);582goto out;583}584585sdp_do_posts(ssk);586out:587SDP_WUNLOCK(ssk);588}589590void591sdp_do_posts(struct sdp_sock *ssk)592{593struct socket *sk = ssk->socket;594int xmit_poll_force;595struct mbuf *mb;596597SDP_WLOCK_ASSERT(ssk);598if (!ssk->qp_active) {599sdp_dbg(sk, "QP is deactivated\n");600return;601}602603while ((mb = mbufq_dequeue(&ssk->rxctlq)) != NULL)604sdp_process_rx_ctl_mb(ssk, mb);605606if (ssk->state == TCPS_TIME_WAIT)607return;608609if (!ssk->rx_ring.cq || !ssk->tx_ring.cq)610return;611612sdp_post_recvs(ssk);613614if (tx_ring_posted(ssk))615sdp_xmit_poll(ssk, 1);616617sdp_post_sends(ssk, M_NOWAIT);618619xmit_poll_force = tx_credits(ssk) < SDP_MIN_TX_CREDITS;620621if (credit_update_needed(ssk) || xmit_poll_force) {622/* if has pending tx because run out of tx_credits - xmit it */623sdp_prf(sk, NULL, "Processing to free pending sends");624sdp_xmit_poll(ssk, xmit_poll_force);625sdp_prf(sk, NULL, "Sending credit update");626sdp_post_sends(ssk, M_NOWAIT);627}628629}630631int632sdp_process_rx(struct sdp_sock *ssk)633{634int wc_processed = 0;635int credits_before;636637if (!rx_ring_trylock(&ssk->rx_ring)) {638sdp_dbg(ssk->socket, "ring destroyed. not polling it\n");639return 0;640}641642credits_before = tx_credits(ssk);643644wc_processed = sdp_poll_rx_cq(ssk);645sdp_prf(ssk->socket, NULL, "processed %d", wc_processed);646647if (wc_processed) {648sdp_prf(ssk->socket, NULL, "credits: %d -> %d",649credits_before, tx_credits(ssk));650queue_work(rx_comp_wq, &ssk->rx_comp_work);651}652sdp_arm_rx_cq(ssk);653654rx_ring_unlock(&ssk->rx_ring);655656return (wc_processed);657}658659static void660sdp_rx_irq(struct ib_cq *cq, void *cq_context)661{662struct sdp_sock *ssk;663664ssk = cq_context;665KASSERT(cq == ssk->rx_ring.cq,666("%s: mismatched cq on %p", __func__, ssk));667668SDPSTATS_COUNTER_INC(rx_int_count);669670sdp_prf(sk, NULL, "rx irq");671672sdp_process_rx(ssk);673}674675static676void sdp_rx_ring_purge(struct sdp_sock *ssk)677{678while (rx_ring_posted(ssk) > 0) {679struct mbuf *mb;680mb = sdp_recv_completion(ssk, ring_tail(ssk->rx_ring));681if (!mb)682break;683m_freem(mb);684}685}686687void688sdp_rx_ring_init(struct sdp_sock *ssk)689{690ssk->rx_ring.buffer = NULL;691ssk->rx_ring.destroyed = 0;692rw_init(&ssk->rx_ring.destroyed_lock, "sdp rx lock");693}694695static void696sdp_rx_cq_event_handler(struct ib_event *event, void *data)697{698}699700int701sdp_rx_ring_create(struct sdp_sock *ssk, struct ib_device *device)702{703struct ib_cq_init_attr rx_cq_attr = {704.cqe = SDP_RX_SIZE,705.comp_vector = 0,706.flags = 0,707};708struct ib_cq *rx_cq;709int rc = 0;710711sdp_dbg(ssk->socket, "rx ring created");712INIT_WORK(&ssk->rx_comp_work, sdp_rx_comp_work);713atomic_set(&ssk->rx_ring.head, 1);714atomic_set(&ssk->rx_ring.tail, 1);715716ssk->rx_ring.buffer = malloc(sizeof(*ssk->rx_ring.buffer) * SDP_RX_SIZE,717M_SDP, M_WAITOK);718719rx_cq = ib_create_cq(device, sdp_rx_irq, sdp_rx_cq_event_handler,720ssk, &rx_cq_attr);721if (IS_ERR(rx_cq)) {722rc = PTR_ERR(rx_cq);723sdp_warn(ssk->socket, "Unable to allocate RX CQ: %d.\n", rc);724goto err_cq;725}726727sdp_sk(ssk->socket)->rx_ring.cq = rx_cq;728sdp_arm_rx_cq(ssk);729730return 0;731732err_cq:733free(ssk->rx_ring.buffer, M_SDP);734ssk->rx_ring.buffer = NULL;735return rc;736}737738void739sdp_rx_ring_destroy(struct sdp_sock *ssk)740{741742cancel_work_sync(&ssk->rx_comp_work);743rx_ring_destroy_lock(&ssk->rx_ring);744745if (ssk->rx_ring.buffer) {746sdp_rx_ring_purge(ssk);747free(ssk->rx_ring.buffer, M_SDP);748ssk->rx_ring.buffer = NULL;749}750751if (ssk->rx_ring.cq) {752ib_destroy_cq(ssk->rx_ring.cq);753ssk->rx_ring.cq = NULL;754}755756WARN_ON(ring_head(ssk->rx_ring) != ring_tail(ssk->rx_ring));757}758759760