162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 or BSD-3-Clause 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci/* Authors: Bernard Metzler <bmt@zurich.ibm.com> */ 462306a36Sopenharmony_ci/* Fredy Neeser */ 562306a36Sopenharmony_ci/* Greg Joyce <greg@opengridcomputing.com> */ 662306a36Sopenharmony_ci/* Copyright (c) 2008-2019, IBM Corporation */ 762306a36Sopenharmony_ci/* Copyright (c) 2017, Open Grid Computing, Inc. */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/errno.h> 1062306a36Sopenharmony_ci#include <linux/types.h> 1162306a36Sopenharmony_ci#include <linux/net.h> 1262306a36Sopenharmony_ci#include <linux/inetdevice.h> 1362306a36Sopenharmony_ci#include <net/addrconf.h> 1462306a36Sopenharmony_ci#include <linux/workqueue.h> 1562306a36Sopenharmony_ci#include <net/sock.h> 1662306a36Sopenharmony_ci#include <net/tcp.h> 1762306a36Sopenharmony_ci#include <linux/inet.h> 1862306a36Sopenharmony_ci#include <linux/tcp.h> 1962306a36Sopenharmony_ci#include <trace/events/sock.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include <rdma/iw_cm.h> 2262306a36Sopenharmony_ci#include <rdma/ib_verbs.h> 2362306a36Sopenharmony_ci#include <rdma/ib_user_verbs.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include "siw.h" 2662306a36Sopenharmony_ci#include "siw_cm.h" 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci/* 2962306a36Sopenharmony_ci * Set to any combination of 3062306a36Sopenharmony_ci * MPA_V2_RDMA_NO_RTR, MPA_V2_RDMA_READ_RTR, MPA_V2_RDMA_WRITE_RTR 3162306a36Sopenharmony_ci */ 3262306a36Sopenharmony_cistatic __be16 rtr_type = MPA_V2_RDMA_READ_RTR | MPA_V2_RDMA_WRITE_RTR; 3362306a36Sopenharmony_cistatic const bool relaxed_ird_negotiation = true; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic void siw_cm_llp_state_change(struct sock *s); 3662306a36Sopenharmony_cistatic void siw_cm_llp_data_ready(struct sock *s); 3762306a36Sopenharmony_cistatic void siw_cm_llp_write_space(struct sock *s); 3862306a36Sopenharmony_cistatic void siw_cm_llp_error_report(struct sock *s); 3962306a36Sopenharmony_cistatic int siw_cm_upcall(struct siw_cep *cep, enum iw_cm_event_type reason, 4062306a36Sopenharmony_ci int status); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic void siw_sk_assign_cm_upcalls(struct sock *sk) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci write_lock_bh(&sk->sk_callback_lock); 4562306a36Sopenharmony_ci sk->sk_state_change = siw_cm_llp_state_change; 4662306a36Sopenharmony_ci sk->sk_data_ready = siw_cm_llp_data_ready; 4762306a36Sopenharmony_ci sk->sk_write_space = siw_cm_llp_write_space; 4862306a36Sopenharmony_ci sk->sk_error_report = siw_cm_llp_error_report; 4962306a36Sopenharmony_ci write_unlock_bh(&sk->sk_callback_lock); 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic void siw_sk_save_upcalls(struct sock *sk) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci struct siw_cep *cep = sk_to_cep(sk); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci write_lock_bh(&sk->sk_callback_lock); 5762306a36Sopenharmony_ci cep->sk_state_change = sk->sk_state_change; 5862306a36Sopenharmony_ci cep->sk_data_ready = sk->sk_data_ready; 5962306a36Sopenharmony_ci cep->sk_write_space = sk->sk_write_space; 6062306a36Sopenharmony_ci cep->sk_error_report = sk->sk_error_report; 6162306a36Sopenharmony_ci write_unlock_bh(&sk->sk_callback_lock); 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic void siw_sk_restore_upcalls(struct sock *sk, struct siw_cep *cep) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci sk->sk_state_change = cep->sk_state_change; 6762306a36Sopenharmony_ci sk->sk_data_ready = cep->sk_data_ready; 6862306a36Sopenharmony_ci sk->sk_write_space = cep->sk_write_space; 6962306a36Sopenharmony_ci sk->sk_error_report = cep->sk_error_report; 7062306a36Sopenharmony_ci sk->sk_user_data = NULL; 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic void siw_qp_socket_assoc(struct siw_cep *cep, struct siw_qp *qp) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci struct socket *s = cep->sock; 7662306a36Sopenharmony_ci struct sock *sk = s->sk; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci write_lock_bh(&sk->sk_callback_lock); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci qp->attrs.sk = s; 8162306a36Sopenharmony_ci sk->sk_data_ready = siw_qp_llp_data_ready; 8262306a36Sopenharmony_ci sk->sk_write_space = siw_qp_llp_write_space; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci write_unlock_bh(&sk->sk_callback_lock); 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic void siw_socket_disassoc(struct socket *s) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci struct sock *sk = s->sk; 9062306a36Sopenharmony_ci struct siw_cep *cep; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci if (sk) { 9362306a36Sopenharmony_ci write_lock_bh(&sk->sk_callback_lock); 9462306a36Sopenharmony_ci cep = sk_to_cep(sk); 9562306a36Sopenharmony_ci if (cep) { 9662306a36Sopenharmony_ci siw_sk_restore_upcalls(sk, cep); 9762306a36Sopenharmony_ci siw_cep_put(cep); 9862306a36Sopenharmony_ci } else { 9962306a36Sopenharmony_ci pr_warn("siw: cannot restore sk callbacks: no ep\n"); 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci write_unlock_bh(&sk->sk_callback_lock); 10262306a36Sopenharmony_ci } else { 10362306a36Sopenharmony_ci pr_warn("siw: cannot restore sk callbacks: no sk\n"); 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic void siw_rtr_data_ready(struct sock *sk) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci struct siw_cep *cep; 11062306a36Sopenharmony_ci struct siw_qp *qp = NULL; 11162306a36Sopenharmony_ci read_descriptor_t rd_desc; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci trace_sk_data_ready(sk); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci read_lock(&sk->sk_callback_lock); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci cep = sk_to_cep(sk); 11862306a36Sopenharmony_ci if (!cep) { 11962306a36Sopenharmony_ci WARN(1, "No connection endpoint\n"); 12062306a36Sopenharmony_ci goto out; 12162306a36Sopenharmony_ci } 12262306a36Sopenharmony_ci qp = sk_to_qp(sk); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci memset(&rd_desc, 0, sizeof(rd_desc)); 12562306a36Sopenharmony_ci rd_desc.arg.data = qp; 12662306a36Sopenharmony_ci rd_desc.count = 1; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci tcp_read_sock(sk, &rd_desc, siw_tcp_rx_data); 12962306a36Sopenharmony_ci /* 13062306a36Sopenharmony_ci * Check if first frame was successfully processed. 13162306a36Sopenharmony_ci * Signal connection full establishment if yes. 13262306a36Sopenharmony_ci * Failed data processing would have already scheduled 13362306a36Sopenharmony_ci * connection drop. 13462306a36Sopenharmony_ci */ 13562306a36Sopenharmony_ci if (!qp->rx_stream.rx_suspend) 13662306a36Sopenharmony_ci siw_cm_upcall(cep, IW_CM_EVENT_ESTABLISHED, 0); 13762306a36Sopenharmony_ciout: 13862306a36Sopenharmony_ci read_unlock(&sk->sk_callback_lock); 13962306a36Sopenharmony_ci if (qp) 14062306a36Sopenharmony_ci siw_qp_socket_assoc(cep, qp); 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic void siw_sk_assign_rtr_upcalls(struct siw_cep *cep) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci struct sock *sk = cep->sock->sk; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci write_lock_bh(&sk->sk_callback_lock); 14862306a36Sopenharmony_ci sk->sk_data_ready = siw_rtr_data_ready; 14962306a36Sopenharmony_ci sk->sk_write_space = siw_qp_llp_write_space; 15062306a36Sopenharmony_ci write_unlock_bh(&sk->sk_callback_lock); 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistatic void siw_cep_socket_assoc(struct siw_cep *cep, struct socket *s) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci cep->sock = s; 15662306a36Sopenharmony_ci siw_cep_get(cep); 15762306a36Sopenharmony_ci s->sk->sk_user_data = cep; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci siw_sk_save_upcalls(s->sk); 16062306a36Sopenharmony_ci siw_sk_assign_cm_upcalls(s->sk); 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_cistatic struct siw_cep *siw_cep_alloc(struct siw_device *sdev) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci struct siw_cep *cep = kzalloc(sizeof(*cep), GFP_KERNEL); 16662306a36Sopenharmony_ci unsigned long flags; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci if (!cep) 16962306a36Sopenharmony_ci return NULL; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci INIT_LIST_HEAD(&cep->listenq); 17262306a36Sopenharmony_ci INIT_LIST_HEAD(&cep->devq); 17362306a36Sopenharmony_ci INIT_LIST_HEAD(&cep->work_freelist); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci kref_init(&cep->ref); 17662306a36Sopenharmony_ci cep->state = SIW_EPSTATE_IDLE; 17762306a36Sopenharmony_ci init_waitqueue_head(&cep->waitq); 17862306a36Sopenharmony_ci spin_lock_init(&cep->lock); 17962306a36Sopenharmony_ci cep->sdev = sdev; 18062306a36Sopenharmony_ci cep->enhanced_rdma_conn_est = false; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci spin_lock_irqsave(&sdev->lock, flags); 18362306a36Sopenharmony_ci list_add_tail(&cep->devq, &sdev->cep_list); 18462306a36Sopenharmony_ci spin_unlock_irqrestore(&sdev->lock, flags); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci siw_dbg_cep(cep, "new endpoint\n"); 18762306a36Sopenharmony_ci return cep; 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic void siw_cm_free_work(struct siw_cep *cep) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci struct list_head *w, *tmp; 19362306a36Sopenharmony_ci struct siw_cm_work *work; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci list_for_each_safe(w, tmp, &cep->work_freelist) { 19662306a36Sopenharmony_ci work = list_entry(w, struct siw_cm_work, list); 19762306a36Sopenharmony_ci list_del(&work->list); 19862306a36Sopenharmony_ci kfree(work); 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cistatic void siw_cancel_mpatimer(struct siw_cep *cep) 20362306a36Sopenharmony_ci{ 20462306a36Sopenharmony_ci spin_lock_bh(&cep->lock); 20562306a36Sopenharmony_ci if (cep->mpa_timer) { 20662306a36Sopenharmony_ci if (cancel_delayed_work(&cep->mpa_timer->work)) { 20762306a36Sopenharmony_ci siw_cep_put(cep); 20862306a36Sopenharmony_ci kfree(cep->mpa_timer); /* not needed again */ 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci cep->mpa_timer = NULL; 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci spin_unlock_bh(&cep->lock); 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_cistatic void siw_put_work(struct siw_cm_work *work) 21662306a36Sopenharmony_ci{ 21762306a36Sopenharmony_ci INIT_LIST_HEAD(&work->list); 21862306a36Sopenharmony_ci spin_lock_bh(&work->cep->lock); 21962306a36Sopenharmony_ci list_add(&work->list, &work->cep->work_freelist); 22062306a36Sopenharmony_ci spin_unlock_bh(&work->cep->lock); 22162306a36Sopenharmony_ci} 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_cistatic void siw_cep_set_inuse(struct siw_cep *cep) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci unsigned long flags; 22662306a36Sopenharmony_ciretry: 22762306a36Sopenharmony_ci spin_lock_irqsave(&cep->lock, flags); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci if (cep->in_use) { 23062306a36Sopenharmony_ci spin_unlock_irqrestore(&cep->lock, flags); 23162306a36Sopenharmony_ci wait_event_interruptible(cep->waitq, !cep->in_use); 23262306a36Sopenharmony_ci if (signal_pending(current)) 23362306a36Sopenharmony_ci flush_signals(current); 23462306a36Sopenharmony_ci goto retry; 23562306a36Sopenharmony_ci } else { 23662306a36Sopenharmony_ci cep->in_use = 1; 23762306a36Sopenharmony_ci spin_unlock_irqrestore(&cep->lock, flags); 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistatic void siw_cep_set_free(struct siw_cep *cep) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci unsigned long flags; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci spin_lock_irqsave(&cep->lock, flags); 24662306a36Sopenharmony_ci cep->in_use = 0; 24762306a36Sopenharmony_ci spin_unlock_irqrestore(&cep->lock, flags); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci wake_up(&cep->waitq); 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_cistatic void __siw_cep_dealloc(struct kref *ref) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci struct siw_cep *cep = container_of(ref, struct siw_cep, ref); 25562306a36Sopenharmony_ci struct siw_device *sdev = cep->sdev; 25662306a36Sopenharmony_ci unsigned long flags; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci WARN_ON(cep->listen_cep); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci /* kfree(NULL) is safe */ 26162306a36Sopenharmony_ci kfree(cep->mpa.pdata); 26262306a36Sopenharmony_ci spin_lock_bh(&cep->lock); 26362306a36Sopenharmony_ci if (!list_empty(&cep->work_freelist)) 26462306a36Sopenharmony_ci siw_cm_free_work(cep); 26562306a36Sopenharmony_ci spin_unlock_bh(&cep->lock); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci spin_lock_irqsave(&sdev->lock, flags); 26862306a36Sopenharmony_ci list_del(&cep->devq); 26962306a36Sopenharmony_ci spin_unlock_irqrestore(&sdev->lock, flags); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci siw_dbg_cep(cep, "free endpoint\n"); 27262306a36Sopenharmony_ci kfree(cep); 27362306a36Sopenharmony_ci} 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_cistatic struct siw_cm_work *siw_get_work(struct siw_cep *cep) 27662306a36Sopenharmony_ci{ 27762306a36Sopenharmony_ci struct siw_cm_work *work = NULL; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci spin_lock_bh(&cep->lock); 28062306a36Sopenharmony_ci if (!list_empty(&cep->work_freelist)) { 28162306a36Sopenharmony_ci work = list_entry(cep->work_freelist.next, struct siw_cm_work, 28262306a36Sopenharmony_ci list); 28362306a36Sopenharmony_ci list_del_init(&work->list); 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci spin_unlock_bh(&cep->lock); 28662306a36Sopenharmony_ci return work; 28762306a36Sopenharmony_ci} 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_cistatic int siw_cm_alloc_work(struct siw_cep *cep, int num) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci struct siw_cm_work *work; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci while (num--) { 29462306a36Sopenharmony_ci work = kmalloc(sizeof(*work), GFP_KERNEL); 29562306a36Sopenharmony_ci if (!work) { 29662306a36Sopenharmony_ci if (!(list_empty(&cep->work_freelist))) 29762306a36Sopenharmony_ci siw_cm_free_work(cep); 29862306a36Sopenharmony_ci return -ENOMEM; 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci work->cep = cep; 30162306a36Sopenharmony_ci INIT_LIST_HEAD(&work->list); 30262306a36Sopenharmony_ci list_add(&work->list, &cep->work_freelist); 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci return 0; 30562306a36Sopenharmony_ci} 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci/* 30862306a36Sopenharmony_ci * siw_cm_upcall() 30962306a36Sopenharmony_ci * 31062306a36Sopenharmony_ci * Upcall to IWCM to inform about async connection events 31162306a36Sopenharmony_ci */ 31262306a36Sopenharmony_cistatic int siw_cm_upcall(struct siw_cep *cep, enum iw_cm_event_type reason, 31362306a36Sopenharmony_ci int status) 31462306a36Sopenharmony_ci{ 31562306a36Sopenharmony_ci struct iw_cm_event event; 31662306a36Sopenharmony_ci struct iw_cm_id *id; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci memset(&event, 0, sizeof(event)); 31962306a36Sopenharmony_ci event.status = status; 32062306a36Sopenharmony_ci event.event = reason; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci if (reason == IW_CM_EVENT_CONNECT_REQUEST) { 32362306a36Sopenharmony_ci event.provider_data = cep; 32462306a36Sopenharmony_ci id = cep->listen_cep->cm_id; 32562306a36Sopenharmony_ci } else { 32662306a36Sopenharmony_ci id = cep->cm_id; 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci /* Signal IRD and ORD */ 32962306a36Sopenharmony_ci if (reason == IW_CM_EVENT_ESTABLISHED || 33062306a36Sopenharmony_ci reason == IW_CM_EVENT_CONNECT_REPLY) { 33162306a36Sopenharmony_ci /* Signal negotiated IRD/ORD values we will use */ 33262306a36Sopenharmony_ci event.ird = cep->ird; 33362306a36Sopenharmony_ci event.ord = cep->ord; 33462306a36Sopenharmony_ci } else if (reason == IW_CM_EVENT_CONNECT_REQUEST) { 33562306a36Sopenharmony_ci event.ird = cep->ord; 33662306a36Sopenharmony_ci event.ord = cep->ird; 33762306a36Sopenharmony_ci } 33862306a36Sopenharmony_ci /* Signal private data and address information */ 33962306a36Sopenharmony_ci if (reason == IW_CM_EVENT_CONNECT_REQUEST || 34062306a36Sopenharmony_ci reason == IW_CM_EVENT_CONNECT_REPLY) { 34162306a36Sopenharmony_ci u16 pd_len = be16_to_cpu(cep->mpa.hdr.params.pd_len); 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci if (pd_len) { 34462306a36Sopenharmony_ci /* 34562306a36Sopenharmony_ci * hand over MPA private data 34662306a36Sopenharmony_ci */ 34762306a36Sopenharmony_ci event.private_data_len = pd_len; 34862306a36Sopenharmony_ci event.private_data = cep->mpa.pdata; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci /* Hide MPA V2 IRD/ORD control */ 35162306a36Sopenharmony_ci if (cep->enhanced_rdma_conn_est) { 35262306a36Sopenharmony_ci event.private_data_len -= 35362306a36Sopenharmony_ci sizeof(struct mpa_v2_data); 35462306a36Sopenharmony_ci event.private_data += 35562306a36Sopenharmony_ci sizeof(struct mpa_v2_data); 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci getname_local(cep->sock, &event.local_addr); 35962306a36Sopenharmony_ci getname_peer(cep->sock, &event.remote_addr); 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci siw_dbg_cep(cep, "[QP %u]: reason=%d, status=%d\n", 36262306a36Sopenharmony_ci cep->qp ? qp_id(cep->qp) : UINT_MAX, reason, status); 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci return id->event_handler(id, &event); 36562306a36Sopenharmony_ci} 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci/* 36862306a36Sopenharmony_ci * siw_qp_cm_drop() 36962306a36Sopenharmony_ci * 37062306a36Sopenharmony_ci * Drops established LLP connection if present and not already 37162306a36Sopenharmony_ci * scheduled for dropping. Called from user context, SQ workqueue 37262306a36Sopenharmony_ci * or receive IRQ. Caller signals if socket can be immediately 37362306a36Sopenharmony_ci * closed (basically, if not in IRQ). 37462306a36Sopenharmony_ci */ 37562306a36Sopenharmony_civoid siw_qp_cm_drop(struct siw_qp *qp, int schedule) 37662306a36Sopenharmony_ci{ 37762306a36Sopenharmony_ci struct siw_cep *cep = qp->cep; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci qp->rx_stream.rx_suspend = 1; 38062306a36Sopenharmony_ci qp->tx_ctx.tx_suspend = 1; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci if (!qp->cep) 38362306a36Sopenharmony_ci return; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci if (schedule) { 38662306a36Sopenharmony_ci siw_cm_queue_work(cep, SIW_CM_WORK_CLOSE_LLP); 38762306a36Sopenharmony_ci } else { 38862306a36Sopenharmony_ci siw_cep_set_inuse(cep); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci if (cep->state == SIW_EPSTATE_CLOSED) { 39162306a36Sopenharmony_ci siw_dbg_cep(cep, "already closed\n"); 39262306a36Sopenharmony_ci goto out; 39362306a36Sopenharmony_ci } 39462306a36Sopenharmony_ci siw_dbg_cep(cep, "immediate close, state %d\n", cep->state); 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci if (qp->term_info.valid) 39762306a36Sopenharmony_ci siw_send_terminate(qp); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci if (cep->cm_id) { 40062306a36Sopenharmony_ci switch (cep->state) { 40162306a36Sopenharmony_ci case SIW_EPSTATE_AWAIT_MPAREP: 40262306a36Sopenharmony_ci siw_cm_upcall(cep, IW_CM_EVENT_CONNECT_REPLY, 40362306a36Sopenharmony_ci -EINVAL); 40462306a36Sopenharmony_ci break; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci case SIW_EPSTATE_RDMA_MODE: 40762306a36Sopenharmony_ci siw_cm_upcall(cep, IW_CM_EVENT_CLOSE, 0); 40862306a36Sopenharmony_ci break; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci case SIW_EPSTATE_IDLE: 41162306a36Sopenharmony_ci case SIW_EPSTATE_LISTENING: 41262306a36Sopenharmony_ci case SIW_EPSTATE_CONNECTING: 41362306a36Sopenharmony_ci case SIW_EPSTATE_AWAIT_MPAREQ: 41462306a36Sopenharmony_ci case SIW_EPSTATE_RECVD_MPAREQ: 41562306a36Sopenharmony_ci case SIW_EPSTATE_CLOSED: 41662306a36Sopenharmony_ci default: 41762306a36Sopenharmony_ci break; 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci cep->cm_id->rem_ref(cep->cm_id); 42062306a36Sopenharmony_ci cep->cm_id = NULL; 42162306a36Sopenharmony_ci siw_cep_put(cep); 42262306a36Sopenharmony_ci } 42362306a36Sopenharmony_ci cep->state = SIW_EPSTATE_CLOSED; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci if (cep->sock) { 42662306a36Sopenharmony_ci siw_socket_disassoc(cep->sock); 42762306a36Sopenharmony_ci /* 42862306a36Sopenharmony_ci * Immediately close socket 42962306a36Sopenharmony_ci */ 43062306a36Sopenharmony_ci sock_release(cep->sock); 43162306a36Sopenharmony_ci cep->sock = NULL; 43262306a36Sopenharmony_ci } 43362306a36Sopenharmony_ci if (cep->qp) { 43462306a36Sopenharmony_ci cep->qp = NULL; 43562306a36Sopenharmony_ci siw_qp_put(qp); 43662306a36Sopenharmony_ci } 43762306a36Sopenharmony_ciout: 43862306a36Sopenharmony_ci siw_cep_set_free(cep); 43962306a36Sopenharmony_ci } 44062306a36Sopenharmony_ci} 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_civoid siw_cep_put(struct siw_cep *cep) 44362306a36Sopenharmony_ci{ 44462306a36Sopenharmony_ci WARN_ON(kref_read(&cep->ref) < 1); 44562306a36Sopenharmony_ci kref_put(&cep->ref, __siw_cep_dealloc); 44662306a36Sopenharmony_ci} 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_civoid siw_cep_get(struct siw_cep *cep) 44962306a36Sopenharmony_ci{ 45062306a36Sopenharmony_ci kref_get(&cep->ref); 45162306a36Sopenharmony_ci} 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci/* 45462306a36Sopenharmony_ci * Expects params->pd_len in host byte order 45562306a36Sopenharmony_ci */ 45662306a36Sopenharmony_cistatic int siw_send_mpareqrep(struct siw_cep *cep, const void *pdata, u8 pd_len) 45762306a36Sopenharmony_ci{ 45862306a36Sopenharmony_ci struct socket *s = cep->sock; 45962306a36Sopenharmony_ci struct mpa_rr *rr = &cep->mpa.hdr; 46062306a36Sopenharmony_ci struct kvec iov[3]; 46162306a36Sopenharmony_ci struct msghdr msg; 46262306a36Sopenharmony_ci int rv; 46362306a36Sopenharmony_ci int iovec_num = 0; 46462306a36Sopenharmony_ci int mpa_len; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci memset(&msg, 0, sizeof(msg)); 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci iov[iovec_num].iov_base = rr; 46962306a36Sopenharmony_ci iov[iovec_num].iov_len = sizeof(*rr); 47062306a36Sopenharmony_ci mpa_len = sizeof(*rr); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci if (cep->enhanced_rdma_conn_est) { 47362306a36Sopenharmony_ci iovec_num++; 47462306a36Sopenharmony_ci iov[iovec_num].iov_base = &cep->mpa.v2_ctrl; 47562306a36Sopenharmony_ci iov[iovec_num].iov_len = sizeof(cep->mpa.v2_ctrl); 47662306a36Sopenharmony_ci mpa_len += sizeof(cep->mpa.v2_ctrl); 47762306a36Sopenharmony_ci } 47862306a36Sopenharmony_ci if (pd_len) { 47962306a36Sopenharmony_ci iovec_num++; 48062306a36Sopenharmony_ci iov[iovec_num].iov_base = (char *)pdata; 48162306a36Sopenharmony_ci iov[iovec_num].iov_len = pd_len; 48262306a36Sopenharmony_ci mpa_len += pd_len; 48362306a36Sopenharmony_ci } 48462306a36Sopenharmony_ci if (cep->enhanced_rdma_conn_est) 48562306a36Sopenharmony_ci pd_len += sizeof(cep->mpa.v2_ctrl); 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci rr->params.pd_len = cpu_to_be16(pd_len); 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci rv = kernel_sendmsg(s, &msg, iov, iovec_num + 1, mpa_len); 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci return rv < 0 ? rv : 0; 49262306a36Sopenharmony_ci} 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci/* 49562306a36Sopenharmony_ci * Receive MPA Request/Reply header. 49662306a36Sopenharmony_ci * 49762306a36Sopenharmony_ci * Returns 0 if complete MPA Request/Reply header including 49862306a36Sopenharmony_ci * eventual private data was received. Returns -EAGAIN if 49962306a36Sopenharmony_ci * header was partially received or negative error code otherwise. 50062306a36Sopenharmony_ci * 50162306a36Sopenharmony_ci * Context: May be called in process context only 50262306a36Sopenharmony_ci */ 50362306a36Sopenharmony_cistatic int siw_recv_mpa_rr(struct siw_cep *cep) 50462306a36Sopenharmony_ci{ 50562306a36Sopenharmony_ci struct mpa_rr *hdr = &cep->mpa.hdr; 50662306a36Sopenharmony_ci struct socket *s = cep->sock; 50762306a36Sopenharmony_ci u16 pd_len; 50862306a36Sopenharmony_ci int rcvd, to_rcv; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci if (cep->mpa.bytes_rcvd < sizeof(struct mpa_rr)) { 51162306a36Sopenharmony_ci rcvd = ksock_recv(s, (char *)hdr + cep->mpa.bytes_rcvd, 51262306a36Sopenharmony_ci sizeof(struct mpa_rr) - cep->mpa.bytes_rcvd, 51362306a36Sopenharmony_ci 0); 51462306a36Sopenharmony_ci if (rcvd <= 0) 51562306a36Sopenharmony_ci return -ECONNABORTED; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci cep->mpa.bytes_rcvd += rcvd; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci if (cep->mpa.bytes_rcvd < sizeof(struct mpa_rr)) 52062306a36Sopenharmony_ci return -EAGAIN; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci if (be16_to_cpu(hdr->params.pd_len) > MPA_MAX_PRIVDATA) 52362306a36Sopenharmony_ci return -EPROTO; 52462306a36Sopenharmony_ci } 52562306a36Sopenharmony_ci pd_len = be16_to_cpu(hdr->params.pd_len); 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci /* 52862306a36Sopenharmony_ci * At least the MPA Request/Reply header (frame not including 52962306a36Sopenharmony_ci * private data) has been received. 53062306a36Sopenharmony_ci * Receive (or continue receiving) any private data. 53162306a36Sopenharmony_ci */ 53262306a36Sopenharmony_ci to_rcv = pd_len - (cep->mpa.bytes_rcvd - sizeof(struct mpa_rr)); 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci if (!to_rcv) { 53562306a36Sopenharmony_ci /* 53662306a36Sopenharmony_ci * We must have hdr->params.pd_len == 0 and thus received a 53762306a36Sopenharmony_ci * complete MPA Request/Reply frame. 53862306a36Sopenharmony_ci * Check against peer protocol violation. 53962306a36Sopenharmony_ci */ 54062306a36Sopenharmony_ci u32 word; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci rcvd = ksock_recv(s, (char *)&word, sizeof(word), MSG_DONTWAIT); 54362306a36Sopenharmony_ci if (rcvd == -EAGAIN) 54462306a36Sopenharmony_ci return 0; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci if (rcvd == 0) { 54762306a36Sopenharmony_ci siw_dbg_cep(cep, "peer EOF\n"); 54862306a36Sopenharmony_ci return -EPIPE; 54962306a36Sopenharmony_ci } 55062306a36Sopenharmony_ci if (rcvd < 0) { 55162306a36Sopenharmony_ci siw_dbg_cep(cep, "error: %d\n", rcvd); 55262306a36Sopenharmony_ci return rcvd; 55362306a36Sopenharmony_ci } 55462306a36Sopenharmony_ci siw_dbg_cep(cep, "peer sent extra data: %d\n", rcvd); 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci return -EPROTO; 55762306a36Sopenharmony_ci } 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci /* 56062306a36Sopenharmony_ci * At this point, we must have hdr->params.pd_len != 0. 56162306a36Sopenharmony_ci * A private data buffer gets allocated if hdr->params.pd_len != 0. 56262306a36Sopenharmony_ci */ 56362306a36Sopenharmony_ci if (!cep->mpa.pdata) { 56462306a36Sopenharmony_ci cep->mpa.pdata = kmalloc(pd_len + 4, GFP_KERNEL); 56562306a36Sopenharmony_ci if (!cep->mpa.pdata) 56662306a36Sopenharmony_ci return -ENOMEM; 56762306a36Sopenharmony_ci } 56862306a36Sopenharmony_ci rcvd = ksock_recv( 56962306a36Sopenharmony_ci s, cep->mpa.pdata + cep->mpa.bytes_rcvd - sizeof(struct mpa_rr), 57062306a36Sopenharmony_ci to_rcv + 4, MSG_DONTWAIT); 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci if (rcvd < 0) 57362306a36Sopenharmony_ci return rcvd; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci if (rcvd > to_rcv) 57662306a36Sopenharmony_ci return -EPROTO; 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci cep->mpa.bytes_rcvd += rcvd; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci if (to_rcv == rcvd) { 58162306a36Sopenharmony_ci siw_dbg_cep(cep, "%d bytes private data received\n", pd_len); 58262306a36Sopenharmony_ci return 0; 58362306a36Sopenharmony_ci } 58462306a36Sopenharmony_ci return -EAGAIN; 58562306a36Sopenharmony_ci} 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci/* 58862306a36Sopenharmony_ci * siw_proc_mpareq() 58962306a36Sopenharmony_ci * 59062306a36Sopenharmony_ci * Read MPA Request from socket and signal new connection to IWCM 59162306a36Sopenharmony_ci * if success. Caller must hold lock on corresponding listening CEP. 59262306a36Sopenharmony_ci */ 59362306a36Sopenharmony_cistatic int siw_proc_mpareq(struct siw_cep *cep) 59462306a36Sopenharmony_ci{ 59562306a36Sopenharmony_ci struct mpa_rr *req; 59662306a36Sopenharmony_ci int version, rv; 59762306a36Sopenharmony_ci u16 pd_len; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci rv = siw_recv_mpa_rr(cep); 60062306a36Sopenharmony_ci if (rv) 60162306a36Sopenharmony_ci return rv; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci req = &cep->mpa.hdr; 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci version = __mpa_rr_revision(req->params.bits); 60662306a36Sopenharmony_ci pd_len = be16_to_cpu(req->params.pd_len); 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci if (version > MPA_REVISION_2) 60962306a36Sopenharmony_ci /* allow for 0, 1, and 2 only */ 61062306a36Sopenharmony_ci return -EPROTO; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci if (memcmp(req->key, MPA_KEY_REQ, 16)) 61362306a36Sopenharmony_ci return -EPROTO; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci /* Prepare for sending MPA reply */ 61662306a36Sopenharmony_ci memcpy(req->key, MPA_KEY_REP, 16); 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci if (version == MPA_REVISION_2 && 61962306a36Sopenharmony_ci (req->params.bits & MPA_RR_FLAG_ENHANCED)) { 62062306a36Sopenharmony_ci /* 62162306a36Sopenharmony_ci * MPA version 2 must signal IRD/ORD values and P2P mode 62262306a36Sopenharmony_ci * in private data if header flag MPA_RR_FLAG_ENHANCED 62362306a36Sopenharmony_ci * is set. 62462306a36Sopenharmony_ci */ 62562306a36Sopenharmony_ci if (pd_len < sizeof(struct mpa_v2_data)) 62662306a36Sopenharmony_ci goto reject_conn; 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci cep->enhanced_rdma_conn_est = true; 62962306a36Sopenharmony_ci } 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci /* MPA Markers: currently not supported. Marker TX to be added. */ 63262306a36Sopenharmony_ci if (req->params.bits & MPA_RR_FLAG_MARKERS) 63362306a36Sopenharmony_ci goto reject_conn; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci if (req->params.bits & MPA_RR_FLAG_CRC) { 63662306a36Sopenharmony_ci /* 63762306a36Sopenharmony_ci * RFC 5044, page 27: CRC MUST be used if peer requests it. 63862306a36Sopenharmony_ci * siw specific: 'mpa_crc_strict' parameter to reject 63962306a36Sopenharmony_ci * connection with CRC if local CRC off enforced by 64062306a36Sopenharmony_ci * 'mpa_crc_strict' module parameter. 64162306a36Sopenharmony_ci */ 64262306a36Sopenharmony_ci if (!mpa_crc_required && mpa_crc_strict) 64362306a36Sopenharmony_ci goto reject_conn; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci /* Enable CRC if requested by module parameter */ 64662306a36Sopenharmony_ci if (mpa_crc_required) 64762306a36Sopenharmony_ci req->params.bits |= MPA_RR_FLAG_CRC; 64862306a36Sopenharmony_ci } 64962306a36Sopenharmony_ci if (cep->enhanced_rdma_conn_est) { 65062306a36Sopenharmony_ci struct mpa_v2_data *v2 = (struct mpa_v2_data *)cep->mpa.pdata; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci /* 65362306a36Sopenharmony_ci * Peer requested ORD becomes requested local IRD, 65462306a36Sopenharmony_ci * peer requested IRD becomes requested local ORD. 65562306a36Sopenharmony_ci * IRD and ORD get limited by global maximum values. 65662306a36Sopenharmony_ci */ 65762306a36Sopenharmony_ci cep->ord = ntohs(v2->ird) & MPA_IRD_ORD_MASK; 65862306a36Sopenharmony_ci cep->ord = min(cep->ord, SIW_MAX_ORD_QP); 65962306a36Sopenharmony_ci cep->ird = ntohs(v2->ord) & MPA_IRD_ORD_MASK; 66062306a36Sopenharmony_ci cep->ird = min(cep->ird, SIW_MAX_IRD_QP); 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci /* May get overwritten by locally negotiated values */ 66362306a36Sopenharmony_ci cep->mpa.v2_ctrl.ird = htons(cep->ird); 66462306a36Sopenharmony_ci cep->mpa.v2_ctrl.ord = htons(cep->ord); 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci /* 66762306a36Sopenharmony_ci * Support for peer sent zero length Write or Read to 66862306a36Sopenharmony_ci * let local side enter RTS. Writes are preferred. 66962306a36Sopenharmony_ci * Sends would require pre-posting a Receive and are 67062306a36Sopenharmony_ci * not supported. 67162306a36Sopenharmony_ci * Propose zero length Write if none of Read and Write 67262306a36Sopenharmony_ci * is indicated. 67362306a36Sopenharmony_ci */ 67462306a36Sopenharmony_ci if (v2->ird & MPA_V2_PEER_TO_PEER) { 67562306a36Sopenharmony_ci cep->mpa.v2_ctrl.ird |= MPA_V2_PEER_TO_PEER; 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci if (v2->ord & MPA_V2_RDMA_WRITE_RTR) 67862306a36Sopenharmony_ci cep->mpa.v2_ctrl.ord |= MPA_V2_RDMA_WRITE_RTR; 67962306a36Sopenharmony_ci else if (v2->ord & MPA_V2_RDMA_READ_RTR) 68062306a36Sopenharmony_ci cep->mpa.v2_ctrl.ord |= MPA_V2_RDMA_READ_RTR; 68162306a36Sopenharmony_ci else 68262306a36Sopenharmony_ci cep->mpa.v2_ctrl.ord |= MPA_V2_RDMA_WRITE_RTR; 68362306a36Sopenharmony_ci } 68462306a36Sopenharmony_ci } 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci cep->state = SIW_EPSTATE_RECVD_MPAREQ; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci /* Keep reference until IWCM accepts/rejects */ 68962306a36Sopenharmony_ci siw_cep_get(cep); 69062306a36Sopenharmony_ci rv = siw_cm_upcall(cep, IW_CM_EVENT_CONNECT_REQUEST, 0); 69162306a36Sopenharmony_ci if (rv) 69262306a36Sopenharmony_ci siw_cep_put(cep); 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci return rv; 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_cireject_conn: 69762306a36Sopenharmony_ci siw_dbg_cep(cep, "reject: crc %d:%d:%d, m %d:%d\n", 69862306a36Sopenharmony_ci req->params.bits & MPA_RR_FLAG_CRC ? 1 : 0, 69962306a36Sopenharmony_ci mpa_crc_required, mpa_crc_strict, 70062306a36Sopenharmony_ci req->params.bits & MPA_RR_FLAG_MARKERS ? 1 : 0, 0); 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci req->params.bits &= ~MPA_RR_FLAG_MARKERS; 70362306a36Sopenharmony_ci req->params.bits |= MPA_RR_FLAG_REJECT; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci if (!mpa_crc_required && mpa_crc_strict) 70662306a36Sopenharmony_ci req->params.bits &= ~MPA_RR_FLAG_CRC; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci if (pd_len) 70962306a36Sopenharmony_ci kfree(cep->mpa.pdata); 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci cep->mpa.pdata = NULL; 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci siw_send_mpareqrep(cep, NULL, 0); 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci return -EOPNOTSUPP; 71662306a36Sopenharmony_ci} 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_cistatic int siw_proc_mpareply(struct siw_cep *cep) 71962306a36Sopenharmony_ci{ 72062306a36Sopenharmony_ci struct siw_qp_attrs qp_attrs; 72162306a36Sopenharmony_ci enum siw_qp_attr_mask qp_attr_mask; 72262306a36Sopenharmony_ci struct siw_qp *qp = cep->qp; 72362306a36Sopenharmony_ci struct mpa_rr *rep; 72462306a36Sopenharmony_ci int rv; 72562306a36Sopenharmony_ci u16 rep_ord; 72662306a36Sopenharmony_ci u16 rep_ird; 72762306a36Sopenharmony_ci bool ird_insufficient = false; 72862306a36Sopenharmony_ci enum mpa_v2_ctrl mpa_p2p_mode = MPA_V2_RDMA_NO_RTR; 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci rv = siw_recv_mpa_rr(cep); 73162306a36Sopenharmony_ci if (rv) 73262306a36Sopenharmony_ci goto out_err; 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci siw_cancel_mpatimer(cep); 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci rep = &cep->mpa.hdr; 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci if (__mpa_rr_revision(rep->params.bits) > MPA_REVISION_2) { 73962306a36Sopenharmony_ci /* allow for 0, 1, and 2 only */ 74062306a36Sopenharmony_ci rv = -EPROTO; 74162306a36Sopenharmony_ci goto out_err; 74262306a36Sopenharmony_ci } 74362306a36Sopenharmony_ci if (memcmp(rep->key, MPA_KEY_REP, 16)) { 74462306a36Sopenharmony_ci siw_init_terminate(qp, TERM_ERROR_LAYER_LLP, LLP_ETYPE_MPA, 74562306a36Sopenharmony_ci LLP_ECODE_INVALID_REQ_RESP, 0); 74662306a36Sopenharmony_ci siw_send_terminate(qp); 74762306a36Sopenharmony_ci rv = -EPROTO; 74862306a36Sopenharmony_ci goto out_err; 74962306a36Sopenharmony_ci } 75062306a36Sopenharmony_ci if (rep->params.bits & MPA_RR_FLAG_REJECT) { 75162306a36Sopenharmony_ci siw_dbg_cep(cep, "got mpa reject\n"); 75262306a36Sopenharmony_ci siw_cm_upcall(cep, IW_CM_EVENT_CONNECT_REPLY, -ECONNRESET); 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci return -ECONNRESET; 75562306a36Sopenharmony_ci } 75662306a36Sopenharmony_ci if (try_gso && rep->params.bits & MPA_RR_FLAG_GSO_EXP) { 75762306a36Sopenharmony_ci siw_dbg_cep(cep, "peer allows GSO on TX\n"); 75862306a36Sopenharmony_ci qp->tx_ctx.gso_seg_limit = 0; 75962306a36Sopenharmony_ci } 76062306a36Sopenharmony_ci if ((rep->params.bits & MPA_RR_FLAG_MARKERS) || 76162306a36Sopenharmony_ci (mpa_crc_required && !(rep->params.bits & MPA_RR_FLAG_CRC)) || 76262306a36Sopenharmony_ci (mpa_crc_strict && !mpa_crc_required && 76362306a36Sopenharmony_ci (rep->params.bits & MPA_RR_FLAG_CRC))) { 76462306a36Sopenharmony_ci siw_dbg_cep(cep, "reply unsupp: crc %d:%d:%d, m %d:%d\n", 76562306a36Sopenharmony_ci rep->params.bits & MPA_RR_FLAG_CRC ? 1 : 0, 76662306a36Sopenharmony_ci mpa_crc_required, mpa_crc_strict, 76762306a36Sopenharmony_ci rep->params.bits & MPA_RR_FLAG_MARKERS ? 1 : 0, 0); 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci siw_cm_upcall(cep, IW_CM_EVENT_CONNECT_REPLY, -ECONNREFUSED); 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci return -EINVAL; 77262306a36Sopenharmony_ci } 77362306a36Sopenharmony_ci if (cep->enhanced_rdma_conn_est) { 77462306a36Sopenharmony_ci struct mpa_v2_data *v2; 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci if (__mpa_rr_revision(rep->params.bits) < MPA_REVISION_2 || 77762306a36Sopenharmony_ci !(rep->params.bits & MPA_RR_FLAG_ENHANCED)) { 77862306a36Sopenharmony_ci /* 77962306a36Sopenharmony_ci * Protocol failure: The responder MUST reply with 78062306a36Sopenharmony_ci * MPA version 2 and MUST set MPA_RR_FLAG_ENHANCED. 78162306a36Sopenharmony_ci */ 78262306a36Sopenharmony_ci siw_dbg_cep(cep, "mpa reply error: vers %d, enhcd %d\n", 78362306a36Sopenharmony_ci __mpa_rr_revision(rep->params.bits), 78462306a36Sopenharmony_ci rep->params.bits & MPA_RR_FLAG_ENHANCED ? 78562306a36Sopenharmony_ci 1 : 78662306a36Sopenharmony_ci 0); 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci siw_cm_upcall(cep, IW_CM_EVENT_CONNECT_REPLY, 78962306a36Sopenharmony_ci -ECONNRESET); 79062306a36Sopenharmony_ci return -EINVAL; 79162306a36Sopenharmony_ci } 79262306a36Sopenharmony_ci v2 = (struct mpa_v2_data *)cep->mpa.pdata; 79362306a36Sopenharmony_ci rep_ird = ntohs(v2->ird) & MPA_IRD_ORD_MASK; 79462306a36Sopenharmony_ci rep_ord = ntohs(v2->ord) & MPA_IRD_ORD_MASK; 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci if (cep->ird < rep_ord && 79762306a36Sopenharmony_ci (relaxed_ird_negotiation == false || 79862306a36Sopenharmony_ci rep_ord > cep->sdev->attrs.max_ird)) { 79962306a36Sopenharmony_ci siw_dbg_cep(cep, "ird %d, rep_ord %d, max_ord %d\n", 80062306a36Sopenharmony_ci cep->ird, rep_ord, 80162306a36Sopenharmony_ci cep->sdev->attrs.max_ord); 80262306a36Sopenharmony_ci ird_insufficient = true; 80362306a36Sopenharmony_ci } 80462306a36Sopenharmony_ci if (cep->ord > rep_ird && relaxed_ird_negotiation == false) { 80562306a36Sopenharmony_ci siw_dbg_cep(cep, "ord %d, rep_ird %d\n", cep->ord, 80662306a36Sopenharmony_ci rep_ird); 80762306a36Sopenharmony_ci ird_insufficient = true; 80862306a36Sopenharmony_ci } 80962306a36Sopenharmony_ci /* 81062306a36Sopenharmony_ci * Always report negotiated peer values to user, 81162306a36Sopenharmony_ci * even if IRD/ORD negotiation failed 81262306a36Sopenharmony_ci */ 81362306a36Sopenharmony_ci cep->ird = rep_ord; 81462306a36Sopenharmony_ci cep->ord = rep_ird; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci if (ird_insufficient) { 81762306a36Sopenharmony_ci /* 81862306a36Sopenharmony_ci * If the initiator IRD is insuffient for the 81962306a36Sopenharmony_ci * responder ORD, send a TERM. 82062306a36Sopenharmony_ci */ 82162306a36Sopenharmony_ci siw_init_terminate(qp, TERM_ERROR_LAYER_LLP, 82262306a36Sopenharmony_ci LLP_ETYPE_MPA, 82362306a36Sopenharmony_ci LLP_ECODE_INSUFFICIENT_IRD, 0); 82462306a36Sopenharmony_ci siw_send_terminate(qp); 82562306a36Sopenharmony_ci rv = -ENOMEM; 82662306a36Sopenharmony_ci goto out_err; 82762306a36Sopenharmony_ci } 82862306a36Sopenharmony_ci if (cep->mpa.v2_ctrl_req.ird & MPA_V2_PEER_TO_PEER) 82962306a36Sopenharmony_ci mpa_p2p_mode = 83062306a36Sopenharmony_ci cep->mpa.v2_ctrl_req.ord & 83162306a36Sopenharmony_ci (MPA_V2_RDMA_WRITE_RTR | MPA_V2_RDMA_READ_RTR); 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci /* 83462306a36Sopenharmony_ci * Check if we requested P2P mode, and if peer agrees 83562306a36Sopenharmony_ci */ 83662306a36Sopenharmony_ci if (mpa_p2p_mode != MPA_V2_RDMA_NO_RTR) { 83762306a36Sopenharmony_ci if ((mpa_p2p_mode & v2->ord) == 0) { 83862306a36Sopenharmony_ci /* 83962306a36Sopenharmony_ci * We requested RTR mode(s), but the peer 84062306a36Sopenharmony_ci * did not pick any mode we support. 84162306a36Sopenharmony_ci */ 84262306a36Sopenharmony_ci siw_dbg_cep(cep, 84362306a36Sopenharmony_ci "rtr mode: req %2x, got %2x\n", 84462306a36Sopenharmony_ci mpa_p2p_mode, 84562306a36Sopenharmony_ci v2->ord & (MPA_V2_RDMA_WRITE_RTR | 84662306a36Sopenharmony_ci MPA_V2_RDMA_READ_RTR)); 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci siw_init_terminate(qp, TERM_ERROR_LAYER_LLP, 84962306a36Sopenharmony_ci LLP_ETYPE_MPA, 85062306a36Sopenharmony_ci LLP_ECODE_NO_MATCHING_RTR, 85162306a36Sopenharmony_ci 0); 85262306a36Sopenharmony_ci siw_send_terminate(qp); 85362306a36Sopenharmony_ci rv = -EPROTO; 85462306a36Sopenharmony_ci goto out_err; 85562306a36Sopenharmony_ci } 85662306a36Sopenharmony_ci mpa_p2p_mode = v2->ord & (MPA_V2_RDMA_WRITE_RTR | 85762306a36Sopenharmony_ci MPA_V2_RDMA_READ_RTR); 85862306a36Sopenharmony_ci } 85962306a36Sopenharmony_ci } 86062306a36Sopenharmony_ci memset(&qp_attrs, 0, sizeof(qp_attrs)); 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci if (rep->params.bits & MPA_RR_FLAG_CRC) 86362306a36Sopenharmony_ci qp_attrs.flags = SIW_MPA_CRC; 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci qp_attrs.irq_size = cep->ird; 86662306a36Sopenharmony_ci qp_attrs.orq_size = cep->ord; 86762306a36Sopenharmony_ci qp_attrs.sk = cep->sock; 86862306a36Sopenharmony_ci qp_attrs.state = SIW_QP_STATE_RTS; 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci qp_attr_mask = SIW_QP_ATTR_STATE | SIW_QP_ATTR_LLP_HANDLE | 87162306a36Sopenharmony_ci SIW_QP_ATTR_ORD | SIW_QP_ATTR_IRD | SIW_QP_ATTR_MPA; 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci /* Move socket RX/TX under QP control */ 87462306a36Sopenharmony_ci down_write(&qp->state_lock); 87562306a36Sopenharmony_ci if (qp->attrs.state > SIW_QP_STATE_RTR) { 87662306a36Sopenharmony_ci rv = -EINVAL; 87762306a36Sopenharmony_ci up_write(&qp->state_lock); 87862306a36Sopenharmony_ci goto out_err; 87962306a36Sopenharmony_ci } 88062306a36Sopenharmony_ci rv = siw_qp_modify(qp, &qp_attrs, qp_attr_mask); 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci siw_qp_socket_assoc(cep, qp); 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci up_write(&qp->state_lock); 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci /* Send extra RDMA frame to trigger peer RTS if negotiated */ 88762306a36Sopenharmony_ci if (mpa_p2p_mode != MPA_V2_RDMA_NO_RTR) { 88862306a36Sopenharmony_ci rv = siw_qp_mpa_rts(qp, mpa_p2p_mode); 88962306a36Sopenharmony_ci if (rv) 89062306a36Sopenharmony_ci goto out_err; 89162306a36Sopenharmony_ci } 89262306a36Sopenharmony_ci if (!rv) { 89362306a36Sopenharmony_ci rv = siw_cm_upcall(cep, IW_CM_EVENT_CONNECT_REPLY, 0); 89462306a36Sopenharmony_ci if (!rv) 89562306a36Sopenharmony_ci cep->state = SIW_EPSTATE_RDMA_MODE; 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci return 0; 89862306a36Sopenharmony_ci } 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ciout_err: 90162306a36Sopenharmony_ci if (rv != -EAGAIN) 90262306a36Sopenharmony_ci siw_cm_upcall(cep, IW_CM_EVENT_CONNECT_REPLY, -EINVAL); 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci return rv; 90562306a36Sopenharmony_ci} 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci/* 90862306a36Sopenharmony_ci * siw_accept_newconn - accept an incoming pending connection 90962306a36Sopenharmony_ci * 91062306a36Sopenharmony_ci */ 91162306a36Sopenharmony_cistatic void siw_accept_newconn(struct siw_cep *cep) 91262306a36Sopenharmony_ci{ 91362306a36Sopenharmony_ci struct socket *s = cep->sock; 91462306a36Sopenharmony_ci struct socket *new_s = NULL; 91562306a36Sopenharmony_ci struct siw_cep *new_cep = NULL; 91662306a36Sopenharmony_ci int rv = 0; /* debug only. should disappear */ 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci if (cep->state != SIW_EPSTATE_LISTENING) 91962306a36Sopenharmony_ci goto error; 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci new_cep = siw_cep_alloc(cep->sdev); 92262306a36Sopenharmony_ci if (!new_cep) 92362306a36Sopenharmony_ci goto error; 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci /* 92662306a36Sopenharmony_ci * 4: Allocate a sufficient number of work elements 92762306a36Sopenharmony_ci * to allow concurrent handling of local + peer close 92862306a36Sopenharmony_ci * events, MPA header processing + MPA timeout. 92962306a36Sopenharmony_ci */ 93062306a36Sopenharmony_ci if (siw_cm_alloc_work(new_cep, 4) != 0) 93162306a36Sopenharmony_ci goto error; 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci /* 93462306a36Sopenharmony_ci * Copy saved socket callbacks from listening CEP 93562306a36Sopenharmony_ci * and assign new socket with new CEP 93662306a36Sopenharmony_ci */ 93762306a36Sopenharmony_ci new_cep->sk_state_change = cep->sk_state_change; 93862306a36Sopenharmony_ci new_cep->sk_data_ready = cep->sk_data_ready; 93962306a36Sopenharmony_ci new_cep->sk_write_space = cep->sk_write_space; 94062306a36Sopenharmony_ci new_cep->sk_error_report = cep->sk_error_report; 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci rv = kernel_accept(s, &new_s, O_NONBLOCK); 94362306a36Sopenharmony_ci if (rv != 0) { 94462306a36Sopenharmony_ci /* 94562306a36Sopenharmony_ci * Connection already aborted by peer..? 94662306a36Sopenharmony_ci */ 94762306a36Sopenharmony_ci siw_dbg_cep(cep, "kernel_accept() error: %d\n", rv); 94862306a36Sopenharmony_ci goto error; 94962306a36Sopenharmony_ci } 95062306a36Sopenharmony_ci new_cep->sock = new_s; 95162306a36Sopenharmony_ci siw_cep_get(new_cep); 95262306a36Sopenharmony_ci new_s->sk->sk_user_data = new_cep; 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci if (siw_tcp_nagle == false) 95562306a36Sopenharmony_ci tcp_sock_set_nodelay(new_s->sk); 95662306a36Sopenharmony_ci new_cep->state = SIW_EPSTATE_AWAIT_MPAREQ; 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci rv = siw_cm_queue_work(new_cep, SIW_CM_WORK_MPATIMEOUT); 95962306a36Sopenharmony_ci if (rv) 96062306a36Sopenharmony_ci goto error; 96162306a36Sopenharmony_ci /* 96262306a36Sopenharmony_ci * See siw_proc_mpareq() etc. for the use of new_cep->listen_cep. 96362306a36Sopenharmony_ci */ 96462306a36Sopenharmony_ci new_cep->listen_cep = cep; 96562306a36Sopenharmony_ci siw_cep_get(cep); 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci if (atomic_read(&new_s->sk->sk_rmem_alloc)) { 96862306a36Sopenharmony_ci /* 96962306a36Sopenharmony_ci * MPA REQ already queued 97062306a36Sopenharmony_ci */ 97162306a36Sopenharmony_ci siw_dbg_cep(cep, "immediate mpa request\n"); 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci siw_cep_set_inuse(new_cep); 97462306a36Sopenharmony_ci rv = siw_proc_mpareq(new_cep); 97562306a36Sopenharmony_ci if (rv != -EAGAIN) { 97662306a36Sopenharmony_ci siw_cep_put(cep); 97762306a36Sopenharmony_ci new_cep->listen_cep = NULL; 97862306a36Sopenharmony_ci if (rv) { 97962306a36Sopenharmony_ci siw_cancel_mpatimer(new_cep); 98062306a36Sopenharmony_ci siw_cep_set_free(new_cep); 98162306a36Sopenharmony_ci goto error; 98262306a36Sopenharmony_ci } 98362306a36Sopenharmony_ci } 98462306a36Sopenharmony_ci siw_cep_set_free(new_cep); 98562306a36Sopenharmony_ci } 98662306a36Sopenharmony_ci return; 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_cierror: 98962306a36Sopenharmony_ci if (new_cep) 99062306a36Sopenharmony_ci siw_cep_put(new_cep); 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci if (new_s) { 99362306a36Sopenharmony_ci siw_socket_disassoc(new_s); 99462306a36Sopenharmony_ci sock_release(new_s); 99562306a36Sopenharmony_ci new_cep->sock = NULL; 99662306a36Sopenharmony_ci } 99762306a36Sopenharmony_ci siw_dbg_cep(cep, "error %d\n", rv); 99862306a36Sopenharmony_ci} 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_cistatic void siw_cm_work_handler(struct work_struct *w) 100162306a36Sopenharmony_ci{ 100262306a36Sopenharmony_ci struct siw_cm_work *work; 100362306a36Sopenharmony_ci struct siw_cep *cep; 100462306a36Sopenharmony_ci int release_cep = 0, rv = 0; 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_ci work = container_of(w, struct siw_cm_work, work.work); 100762306a36Sopenharmony_ci cep = work->cep; 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci siw_dbg_cep(cep, "[QP %u]: work type: %d, state %d\n", 101062306a36Sopenharmony_ci cep->qp ? qp_id(cep->qp) : UINT_MAX, 101162306a36Sopenharmony_ci work->type, cep->state); 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci siw_cep_set_inuse(cep); 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci switch (work->type) { 101662306a36Sopenharmony_ci case SIW_CM_WORK_ACCEPT: 101762306a36Sopenharmony_ci siw_accept_newconn(cep); 101862306a36Sopenharmony_ci break; 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci case SIW_CM_WORK_READ_MPAHDR: 102162306a36Sopenharmony_ci if (cep->state == SIW_EPSTATE_AWAIT_MPAREQ) { 102262306a36Sopenharmony_ci if (cep->listen_cep) { 102362306a36Sopenharmony_ci siw_cep_set_inuse(cep->listen_cep); 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci if (cep->listen_cep->state == 102662306a36Sopenharmony_ci SIW_EPSTATE_LISTENING) 102762306a36Sopenharmony_ci rv = siw_proc_mpareq(cep); 102862306a36Sopenharmony_ci else 102962306a36Sopenharmony_ci rv = -EFAULT; 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci siw_cep_set_free(cep->listen_cep); 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci if (rv != -EAGAIN) { 103462306a36Sopenharmony_ci siw_cep_put(cep->listen_cep); 103562306a36Sopenharmony_ci cep->listen_cep = NULL; 103662306a36Sopenharmony_ci if (rv) 103762306a36Sopenharmony_ci siw_cep_put(cep); 103862306a36Sopenharmony_ci } 103962306a36Sopenharmony_ci } 104062306a36Sopenharmony_ci } else if (cep->state == SIW_EPSTATE_AWAIT_MPAREP) { 104162306a36Sopenharmony_ci rv = siw_proc_mpareply(cep); 104262306a36Sopenharmony_ci } else { 104362306a36Sopenharmony_ci /* 104462306a36Sopenharmony_ci * CEP already moved out of MPA handshake. 104562306a36Sopenharmony_ci * any connection management already done. 104662306a36Sopenharmony_ci * silently ignore the mpa packet. 104762306a36Sopenharmony_ci */ 104862306a36Sopenharmony_ci if (cep->state == SIW_EPSTATE_RDMA_MODE) { 104962306a36Sopenharmony_ci cep->sock->sk->sk_data_ready(cep->sock->sk); 105062306a36Sopenharmony_ci siw_dbg_cep(cep, "already in RDMA mode"); 105162306a36Sopenharmony_ci } else { 105262306a36Sopenharmony_ci siw_dbg_cep(cep, "out of state: %d\n", 105362306a36Sopenharmony_ci cep->state); 105462306a36Sopenharmony_ci } 105562306a36Sopenharmony_ci } 105662306a36Sopenharmony_ci if (rv && rv != -EAGAIN) 105762306a36Sopenharmony_ci release_cep = 1; 105862306a36Sopenharmony_ci break; 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci case SIW_CM_WORK_CLOSE_LLP: 106162306a36Sopenharmony_ci /* 106262306a36Sopenharmony_ci * QP scheduled LLP close 106362306a36Sopenharmony_ci */ 106462306a36Sopenharmony_ci if (cep->qp && cep->qp->term_info.valid) 106562306a36Sopenharmony_ci siw_send_terminate(cep->qp); 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci if (cep->cm_id) 106862306a36Sopenharmony_ci siw_cm_upcall(cep, IW_CM_EVENT_CLOSE, 0); 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci release_cep = 1; 107162306a36Sopenharmony_ci break; 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci case SIW_CM_WORK_PEER_CLOSE: 107462306a36Sopenharmony_ci if (cep->cm_id) { 107562306a36Sopenharmony_ci if (cep->state == SIW_EPSTATE_AWAIT_MPAREP) { 107662306a36Sopenharmony_ci /* 107762306a36Sopenharmony_ci * MPA reply not received, but connection drop 107862306a36Sopenharmony_ci */ 107962306a36Sopenharmony_ci siw_cm_upcall(cep, IW_CM_EVENT_CONNECT_REPLY, 108062306a36Sopenharmony_ci -ECONNRESET); 108162306a36Sopenharmony_ci } else if (cep->state == SIW_EPSTATE_RDMA_MODE) { 108262306a36Sopenharmony_ci /* 108362306a36Sopenharmony_ci * NOTE: IW_CM_EVENT_DISCONNECT is given just 108462306a36Sopenharmony_ci * to transition IWCM into CLOSING. 108562306a36Sopenharmony_ci */ 108662306a36Sopenharmony_ci siw_cm_upcall(cep, IW_CM_EVENT_DISCONNECT, 0); 108762306a36Sopenharmony_ci siw_cm_upcall(cep, IW_CM_EVENT_CLOSE, 0); 108862306a36Sopenharmony_ci } 108962306a36Sopenharmony_ci /* 109062306a36Sopenharmony_ci * for other states there is no connection 109162306a36Sopenharmony_ci * known to the IWCM. 109262306a36Sopenharmony_ci */ 109362306a36Sopenharmony_ci } else { 109462306a36Sopenharmony_ci if (cep->state == SIW_EPSTATE_RECVD_MPAREQ) { 109562306a36Sopenharmony_ci /* 109662306a36Sopenharmony_ci * Wait for the ulp/CM to call accept/reject 109762306a36Sopenharmony_ci */ 109862306a36Sopenharmony_ci siw_dbg_cep(cep, 109962306a36Sopenharmony_ci "mpa req recvd, wait for ULP\n"); 110062306a36Sopenharmony_ci } else if (cep->state == SIW_EPSTATE_AWAIT_MPAREQ) { 110162306a36Sopenharmony_ci /* 110262306a36Sopenharmony_ci * Socket close before MPA request received. 110362306a36Sopenharmony_ci */ 110462306a36Sopenharmony_ci if (cep->listen_cep) { 110562306a36Sopenharmony_ci siw_dbg_cep(cep, 110662306a36Sopenharmony_ci "no mpareq: drop listener\n"); 110762306a36Sopenharmony_ci siw_cep_put(cep->listen_cep); 110862306a36Sopenharmony_ci cep->listen_cep = NULL; 110962306a36Sopenharmony_ci } 111062306a36Sopenharmony_ci } 111162306a36Sopenharmony_ci } 111262306a36Sopenharmony_ci release_cep = 1; 111362306a36Sopenharmony_ci break; 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ci case SIW_CM_WORK_MPATIMEOUT: 111662306a36Sopenharmony_ci cep->mpa_timer = NULL; 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_ci if (cep->state == SIW_EPSTATE_AWAIT_MPAREP) { 111962306a36Sopenharmony_ci /* 112062306a36Sopenharmony_ci * MPA request timed out: 112162306a36Sopenharmony_ci * Hide any partially received private data and signal 112262306a36Sopenharmony_ci * timeout 112362306a36Sopenharmony_ci */ 112462306a36Sopenharmony_ci cep->mpa.hdr.params.pd_len = 0; 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci if (cep->cm_id) 112762306a36Sopenharmony_ci siw_cm_upcall(cep, IW_CM_EVENT_CONNECT_REPLY, 112862306a36Sopenharmony_ci -ETIMEDOUT); 112962306a36Sopenharmony_ci release_cep = 1; 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci } else if (cep->state == SIW_EPSTATE_AWAIT_MPAREQ) { 113262306a36Sopenharmony_ci /* 113362306a36Sopenharmony_ci * No MPA request received after peer TCP stream setup. 113462306a36Sopenharmony_ci */ 113562306a36Sopenharmony_ci if (cep->listen_cep) { 113662306a36Sopenharmony_ci siw_cep_put(cep->listen_cep); 113762306a36Sopenharmony_ci cep->listen_cep = NULL; 113862306a36Sopenharmony_ci } 113962306a36Sopenharmony_ci release_cep = 1; 114062306a36Sopenharmony_ci } 114162306a36Sopenharmony_ci break; 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci default: 114462306a36Sopenharmony_ci WARN(1, "Undefined CM work type: %d\n", work->type); 114562306a36Sopenharmony_ci } 114662306a36Sopenharmony_ci if (release_cep) { 114762306a36Sopenharmony_ci siw_dbg_cep(cep, 114862306a36Sopenharmony_ci "release: timer=%s, QP[%u]\n", 114962306a36Sopenharmony_ci cep->mpa_timer ? "y" : "n", 115062306a36Sopenharmony_ci cep->qp ? qp_id(cep->qp) : UINT_MAX); 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ci siw_cancel_mpatimer(cep); 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci cep->state = SIW_EPSTATE_CLOSED; 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ci if (cep->qp) { 115762306a36Sopenharmony_ci struct siw_qp *qp = cep->qp; 115862306a36Sopenharmony_ci /* 115962306a36Sopenharmony_ci * Serialize a potential race with application 116062306a36Sopenharmony_ci * closing the QP and calling siw_qp_cm_drop() 116162306a36Sopenharmony_ci */ 116262306a36Sopenharmony_ci siw_qp_get(qp); 116362306a36Sopenharmony_ci siw_cep_set_free(cep); 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_ci siw_qp_llp_close(qp); 116662306a36Sopenharmony_ci siw_qp_put(qp); 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_ci siw_cep_set_inuse(cep); 116962306a36Sopenharmony_ci cep->qp = NULL; 117062306a36Sopenharmony_ci siw_qp_put(qp); 117162306a36Sopenharmony_ci } 117262306a36Sopenharmony_ci if (cep->sock) { 117362306a36Sopenharmony_ci siw_socket_disassoc(cep->sock); 117462306a36Sopenharmony_ci sock_release(cep->sock); 117562306a36Sopenharmony_ci cep->sock = NULL; 117662306a36Sopenharmony_ci } 117762306a36Sopenharmony_ci if (cep->cm_id) { 117862306a36Sopenharmony_ci cep->cm_id->rem_ref(cep->cm_id); 117962306a36Sopenharmony_ci cep->cm_id = NULL; 118062306a36Sopenharmony_ci siw_cep_put(cep); 118162306a36Sopenharmony_ci } 118262306a36Sopenharmony_ci } 118362306a36Sopenharmony_ci siw_cep_set_free(cep); 118462306a36Sopenharmony_ci siw_put_work(work); 118562306a36Sopenharmony_ci siw_cep_put(cep); 118662306a36Sopenharmony_ci} 118762306a36Sopenharmony_ci 118862306a36Sopenharmony_cistatic struct workqueue_struct *siw_cm_wq; 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ciint siw_cm_queue_work(struct siw_cep *cep, enum siw_work_type type) 119162306a36Sopenharmony_ci{ 119262306a36Sopenharmony_ci struct siw_cm_work *work = siw_get_work(cep); 119362306a36Sopenharmony_ci unsigned long delay = 0; 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci if (!work) { 119662306a36Sopenharmony_ci siw_dbg_cep(cep, "failed with no work available\n"); 119762306a36Sopenharmony_ci return -ENOMEM; 119862306a36Sopenharmony_ci } 119962306a36Sopenharmony_ci work->type = type; 120062306a36Sopenharmony_ci work->cep = cep; 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_ci siw_cep_get(cep); 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ci INIT_DELAYED_WORK(&work->work, siw_cm_work_handler); 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_ci if (type == SIW_CM_WORK_MPATIMEOUT) { 120762306a36Sopenharmony_ci cep->mpa_timer = work; 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_ci if (cep->state == SIW_EPSTATE_AWAIT_MPAREP) 121062306a36Sopenharmony_ci delay = MPAREQ_TIMEOUT; 121162306a36Sopenharmony_ci else 121262306a36Sopenharmony_ci delay = MPAREP_TIMEOUT; 121362306a36Sopenharmony_ci } 121462306a36Sopenharmony_ci siw_dbg_cep(cep, "[QP %u]: work type: %d, timeout %lu\n", 121562306a36Sopenharmony_ci cep->qp ? qp_id(cep->qp) : -1, type, delay); 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_ci queue_delayed_work(siw_cm_wq, &work->work, delay); 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci return 0; 122062306a36Sopenharmony_ci} 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_cistatic void siw_cm_llp_data_ready(struct sock *sk) 122362306a36Sopenharmony_ci{ 122462306a36Sopenharmony_ci struct siw_cep *cep; 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_ci trace_sk_data_ready(sk); 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci read_lock(&sk->sk_callback_lock); 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_ci cep = sk_to_cep(sk); 123162306a36Sopenharmony_ci if (!cep) 123262306a36Sopenharmony_ci goto out; 123362306a36Sopenharmony_ci 123462306a36Sopenharmony_ci siw_dbg_cep(cep, "cep state: %d, socket state %d\n", 123562306a36Sopenharmony_ci cep->state, sk->sk_state); 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_ci if (sk->sk_state != TCP_ESTABLISHED) 123862306a36Sopenharmony_ci goto out; 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_ci switch (cep->state) { 124162306a36Sopenharmony_ci case SIW_EPSTATE_RDMA_MODE: 124262306a36Sopenharmony_ci case SIW_EPSTATE_LISTENING: 124362306a36Sopenharmony_ci break; 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_ci case SIW_EPSTATE_AWAIT_MPAREQ: 124662306a36Sopenharmony_ci case SIW_EPSTATE_AWAIT_MPAREP: 124762306a36Sopenharmony_ci siw_cm_queue_work(cep, SIW_CM_WORK_READ_MPAHDR); 124862306a36Sopenharmony_ci break; 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci default: 125162306a36Sopenharmony_ci siw_dbg_cep(cep, "unexpected data, state %d\n", cep->state); 125262306a36Sopenharmony_ci break; 125362306a36Sopenharmony_ci } 125462306a36Sopenharmony_ciout: 125562306a36Sopenharmony_ci read_unlock(&sk->sk_callback_lock); 125662306a36Sopenharmony_ci} 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_cistatic void siw_cm_llp_write_space(struct sock *sk) 125962306a36Sopenharmony_ci{ 126062306a36Sopenharmony_ci struct siw_cep *cep = sk_to_cep(sk); 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_ci if (cep) 126362306a36Sopenharmony_ci siw_dbg_cep(cep, "state: %d\n", cep->state); 126462306a36Sopenharmony_ci} 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_cistatic void siw_cm_llp_error_report(struct sock *sk) 126762306a36Sopenharmony_ci{ 126862306a36Sopenharmony_ci struct siw_cep *cep = sk_to_cep(sk); 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ci if (cep) { 127162306a36Sopenharmony_ci siw_dbg_cep(cep, "error %d, socket state: %d, cep state: %d\n", 127262306a36Sopenharmony_ci sk->sk_err, sk->sk_state, cep->state); 127362306a36Sopenharmony_ci cep->sk_error_report(sk); 127462306a36Sopenharmony_ci } 127562306a36Sopenharmony_ci} 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_cistatic void siw_cm_llp_state_change(struct sock *sk) 127862306a36Sopenharmony_ci{ 127962306a36Sopenharmony_ci struct siw_cep *cep; 128062306a36Sopenharmony_ci void (*orig_state_change)(struct sock *s); 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_ci read_lock(&sk->sk_callback_lock); 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci cep = sk_to_cep(sk); 128562306a36Sopenharmony_ci if (!cep) { 128662306a36Sopenharmony_ci /* endpoint already disassociated */ 128762306a36Sopenharmony_ci read_unlock(&sk->sk_callback_lock); 128862306a36Sopenharmony_ci return; 128962306a36Sopenharmony_ci } 129062306a36Sopenharmony_ci orig_state_change = cep->sk_state_change; 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ci siw_dbg_cep(cep, "state: %d\n", cep->state); 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ci switch (sk->sk_state) { 129562306a36Sopenharmony_ci case TCP_ESTABLISHED: 129662306a36Sopenharmony_ci /* 129762306a36Sopenharmony_ci * handle accepting socket as special case where only 129862306a36Sopenharmony_ci * new connection is possible 129962306a36Sopenharmony_ci */ 130062306a36Sopenharmony_ci siw_cm_queue_work(cep, SIW_CM_WORK_ACCEPT); 130162306a36Sopenharmony_ci break; 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_ci case TCP_CLOSE: 130462306a36Sopenharmony_ci case TCP_CLOSE_WAIT: 130562306a36Sopenharmony_ci if (cep->qp) 130662306a36Sopenharmony_ci cep->qp->tx_ctx.tx_suspend = 1; 130762306a36Sopenharmony_ci siw_cm_queue_work(cep, SIW_CM_WORK_PEER_CLOSE); 130862306a36Sopenharmony_ci break; 130962306a36Sopenharmony_ci 131062306a36Sopenharmony_ci default: 131162306a36Sopenharmony_ci siw_dbg_cep(cep, "unexpected socket state %d\n", sk->sk_state); 131262306a36Sopenharmony_ci } 131362306a36Sopenharmony_ci read_unlock(&sk->sk_callback_lock); 131462306a36Sopenharmony_ci orig_state_change(sk); 131562306a36Sopenharmony_ci} 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_cistatic int kernel_bindconnect(struct socket *s, struct sockaddr *laddr, 131862306a36Sopenharmony_ci struct sockaddr *raddr, bool afonly) 131962306a36Sopenharmony_ci{ 132062306a36Sopenharmony_ci int rv, flags = 0; 132162306a36Sopenharmony_ci size_t size = laddr->sa_family == AF_INET ? 132262306a36Sopenharmony_ci sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6); 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ci /* 132562306a36Sopenharmony_ci * Make address available again asap. 132662306a36Sopenharmony_ci */ 132762306a36Sopenharmony_ci sock_set_reuseaddr(s->sk); 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_ci if (afonly) { 133062306a36Sopenharmony_ci rv = ip6_sock_set_v6only(s->sk); 133162306a36Sopenharmony_ci if (rv) 133262306a36Sopenharmony_ci return rv; 133362306a36Sopenharmony_ci } 133462306a36Sopenharmony_ci 133562306a36Sopenharmony_ci rv = s->ops->bind(s, laddr, size); 133662306a36Sopenharmony_ci if (rv < 0) 133762306a36Sopenharmony_ci return rv; 133862306a36Sopenharmony_ci 133962306a36Sopenharmony_ci rv = s->ops->connect(s, raddr, size, flags); 134062306a36Sopenharmony_ci 134162306a36Sopenharmony_ci return rv < 0 ? rv : 0; 134262306a36Sopenharmony_ci} 134362306a36Sopenharmony_ci 134462306a36Sopenharmony_ciint siw_connect(struct iw_cm_id *id, struct iw_cm_conn_param *params) 134562306a36Sopenharmony_ci{ 134662306a36Sopenharmony_ci struct siw_device *sdev = to_siw_dev(id->device); 134762306a36Sopenharmony_ci struct siw_qp *qp; 134862306a36Sopenharmony_ci struct siw_cep *cep = NULL; 134962306a36Sopenharmony_ci struct socket *s = NULL; 135062306a36Sopenharmony_ci struct sockaddr *laddr = (struct sockaddr *)&id->local_addr, 135162306a36Sopenharmony_ci *raddr = (struct sockaddr *)&id->remote_addr; 135262306a36Sopenharmony_ci bool p2p_mode = peer_to_peer, v4 = true; 135362306a36Sopenharmony_ci u16 pd_len = params->private_data_len; 135462306a36Sopenharmony_ci int version = mpa_version, rv; 135562306a36Sopenharmony_ci 135662306a36Sopenharmony_ci if (pd_len > MPA_MAX_PRIVDATA) 135762306a36Sopenharmony_ci return -EINVAL; 135862306a36Sopenharmony_ci 135962306a36Sopenharmony_ci if (params->ird > sdev->attrs.max_ird || 136062306a36Sopenharmony_ci params->ord > sdev->attrs.max_ord) 136162306a36Sopenharmony_ci return -ENOMEM; 136262306a36Sopenharmony_ci 136362306a36Sopenharmony_ci if (laddr->sa_family == AF_INET6) 136462306a36Sopenharmony_ci v4 = false; 136562306a36Sopenharmony_ci else if (laddr->sa_family != AF_INET) 136662306a36Sopenharmony_ci return -EAFNOSUPPORT; 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_ci /* 136962306a36Sopenharmony_ci * Respect any iwarp port mapping: Use mapped remote address 137062306a36Sopenharmony_ci * if valid. Local address must not be mapped, since siw 137162306a36Sopenharmony_ci * uses kernel TCP stack. 137262306a36Sopenharmony_ci */ 137362306a36Sopenharmony_ci if ((v4 && to_sockaddr_in(id->remote_addr).sin_port != 0) || 137462306a36Sopenharmony_ci to_sockaddr_in6(id->remote_addr).sin6_port != 0) 137562306a36Sopenharmony_ci raddr = (struct sockaddr *)&id->m_remote_addr; 137662306a36Sopenharmony_ci 137762306a36Sopenharmony_ci qp = siw_qp_id2obj(sdev, params->qpn); 137862306a36Sopenharmony_ci if (!qp) { 137962306a36Sopenharmony_ci WARN(1, "[QP %u] does not exist\n", params->qpn); 138062306a36Sopenharmony_ci rv = -EINVAL; 138162306a36Sopenharmony_ci goto error; 138262306a36Sopenharmony_ci } 138362306a36Sopenharmony_ci siw_dbg_qp(qp, "pd_len %d, laddr %pISp, raddr %pISp\n", pd_len, laddr, 138462306a36Sopenharmony_ci raddr); 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_ci rv = sock_create(v4 ? AF_INET : AF_INET6, SOCK_STREAM, IPPROTO_TCP, &s); 138762306a36Sopenharmony_ci if (rv < 0) 138862306a36Sopenharmony_ci goto error; 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_ci /* 139162306a36Sopenharmony_ci * NOTE: For simplification, connect() is called in blocking 139262306a36Sopenharmony_ci * mode. Might be reconsidered for async connection setup at 139362306a36Sopenharmony_ci * TCP level. 139462306a36Sopenharmony_ci */ 139562306a36Sopenharmony_ci rv = kernel_bindconnect(s, laddr, raddr, id->afonly); 139662306a36Sopenharmony_ci if (rv != 0) { 139762306a36Sopenharmony_ci siw_dbg_qp(qp, "kernel_bindconnect: error %d\n", rv); 139862306a36Sopenharmony_ci goto error; 139962306a36Sopenharmony_ci } 140062306a36Sopenharmony_ci if (siw_tcp_nagle == false) 140162306a36Sopenharmony_ci tcp_sock_set_nodelay(s->sk); 140262306a36Sopenharmony_ci cep = siw_cep_alloc(sdev); 140362306a36Sopenharmony_ci if (!cep) { 140462306a36Sopenharmony_ci rv = -ENOMEM; 140562306a36Sopenharmony_ci goto error; 140662306a36Sopenharmony_ci } 140762306a36Sopenharmony_ci siw_cep_set_inuse(cep); 140862306a36Sopenharmony_ci 140962306a36Sopenharmony_ci /* Associate QP with CEP */ 141062306a36Sopenharmony_ci siw_cep_get(cep); 141162306a36Sopenharmony_ci qp->cep = cep; 141262306a36Sopenharmony_ci 141362306a36Sopenharmony_ci /* siw_qp_get(qp) already done by QP lookup */ 141462306a36Sopenharmony_ci cep->qp = qp; 141562306a36Sopenharmony_ci 141662306a36Sopenharmony_ci id->add_ref(id); 141762306a36Sopenharmony_ci cep->cm_id = id; 141862306a36Sopenharmony_ci 141962306a36Sopenharmony_ci /* 142062306a36Sopenharmony_ci * 4: Allocate a sufficient number of work elements 142162306a36Sopenharmony_ci * to allow concurrent handling of local + peer close 142262306a36Sopenharmony_ci * events, MPA header processing + MPA timeout. 142362306a36Sopenharmony_ci */ 142462306a36Sopenharmony_ci rv = siw_cm_alloc_work(cep, 4); 142562306a36Sopenharmony_ci if (rv != 0) { 142662306a36Sopenharmony_ci rv = -ENOMEM; 142762306a36Sopenharmony_ci goto error; 142862306a36Sopenharmony_ci } 142962306a36Sopenharmony_ci cep->ird = params->ird; 143062306a36Sopenharmony_ci cep->ord = params->ord; 143162306a36Sopenharmony_ci 143262306a36Sopenharmony_ci if (p2p_mode && cep->ord == 0) 143362306a36Sopenharmony_ci cep->ord = 1; 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_ci cep->state = SIW_EPSTATE_CONNECTING; 143662306a36Sopenharmony_ci 143762306a36Sopenharmony_ci /* 143862306a36Sopenharmony_ci * Associate CEP with socket 143962306a36Sopenharmony_ci */ 144062306a36Sopenharmony_ci siw_cep_socket_assoc(cep, s); 144162306a36Sopenharmony_ci 144262306a36Sopenharmony_ci cep->state = SIW_EPSTATE_AWAIT_MPAREP; 144362306a36Sopenharmony_ci 144462306a36Sopenharmony_ci /* 144562306a36Sopenharmony_ci * Set MPA Request bits: CRC if required, no MPA Markers, 144662306a36Sopenharmony_ci * MPA Rev. according to module parameter 'mpa_version', Key 'Request'. 144762306a36Sopenharmony_ci */ 144862306a36Sopenharmony_ci cep->mpa.hdr.params.bits = 0; 144962306a36Sopenharmony_ci if (version > MPA_REVISION_2) { 145062306a36Sopenharmony_ci pr_warn("Setting MPA version to %u\n", MPA_REVISION_2); 145162306a36Sopenharmony_ci version = MPA_REVISION_2; 145262306a36Sopenharmony_ci /* Adjust also module parameter */ 145362306a36Sopenharmony_ci mpa_version = MPA_REVISION_2; 145462306a36Sopenharmony_ci } 145562306a36Sopenharmony_ci __mpa_rr_set_revision(&cep->mpa.hdr.params.bits, version); 145662306a36Sopenharmony_ci 145762306a36Sopenharmony_ci if (try_gso) 145862306a36Sopenharmony_ci cep->mpa.hdr.params.bits |= MPA_RR_FLAG_GSO_EXP; 145962306a36Sopenharmony_ci 146062306a36Sopenharmony_ci if (mpa_crc_required) 146162306a36Sopenharmony_ci cep->mpa.hdr.params.bits |= MPA_RR_FLAG_CRC; 146262306a36Sopenharmony_ci 146362306a36Sopenharmony_ci /* 146462306a36Sopenharmony_ci * If MPA version == 2: 146562306a36Sopenharmony_ci * o Include ORD and IRD. 146662306a36Sopenharmony_ci * o Indicate peer-to-peer mode, if required by module 146762306a36Sopenharmony_ci * parameter 'peer_to_peer'. 146862306a36Sopenharmony_ci */ 146962306a36Sopenharmony_ci if (version == MPA_REVISION_2) { 147062306a36Sopenharmony_ci cep->enhanced_rdma_conn_est = true; 147162306a36Sopenharmony_ci cep->mpa.hdr.params.bits |= MPA_RR_FLAG_ENHANCED; 147262306a36Sopenharmony_ci 147362306a36Sopenharmony_ci cep->mpa.v2_ctrl.ird = htons(cep->ird); 147462306a36Sopenharmony_ci cep->mpa.v2_ctrl.ord = htons(cep->ord); 147562306a36Sopenharmony_ci 147662306a36Sopenharmony_ci if (p2p_mode) { 147762306a36Sopenharmony_ci cep->mpa.v2_ctrl.ird |= MPA_V2_PEER_TO_PEER; 147862306a36Sopenharmony_ci cep->mpa.v2_ctrl.ord |= rtr_type; 147962306a36Sopenharmony_ci } 148062306a36Sopenharmony_ci /* Remember own P2P mode requested */ 148162306a36Sopenharmony_ci cep->mpa.v2_ctrl_req.ird = cep->mpa.v2_ctrl.ird; 148262306a36Sopenharmony_ci cep->mpa.v2_ctrl_req.ord = cep->mpa.v2_ctrl.ord; 148362306a36Sopenharmony_ci } 148462306a36Sopenharmony_ci memcpy(cep->mpa.hdr.key, MPA_KEY_REQ, 16); 148562306a36Sopenharmony_ci 148662306a36Sopenharmony_ci rv = siw_send_mpareqrep(cep, params->private_data, pd_len); 148762306a36Sopenharmony_ci /* 148862306a36Sopenharmony_ci * Reset private data. 148962306a36Sopenharmony_ci */ 149062306a36Sopenharmony_ci cep->mpa.hdr.params.pd_len = 0; 149162306a36Sopenharmony_ci 149262306a36Sopenharmony_ci if (rv >= 0) { 149362306a36Sopenharmony_ci rv = siw_cm_queue_work(cep, SIW_CM_WORK_MPATIMEOUT); 149462306a36Sopenharmony_ci if (!rv) { 149562306a36Sopenharmony_ci siw_dbg_cep(cep, "[QP %u]: exit\n", qp_id(qp)); 149662306a36Sopenharmony_ci siw_cep_set_free(cep); 149762306a36Sopenharmony_ci return 0; 149862306a36Sopenharmony_ci } 149962306a36Sopenharmony_ci } 150062306a36Sopenharmony_cierror: 150162306a36Sopenharmony_ci siw_dbg(id->device, "failed: %d\n", rv); 150262306a36Sopenharmony_ci 150362306a36Sopenharmony_ci if (cep) { 150462306a36Sopenharmony_ci siw_socket_disassoc(s); 150562306a36Sopenharmony_ci sock_release(s); 150662306a36Sopenharmony_ci cep->sock = NULL; 150762306a36Sopenharmony_ci 150862306a36Sopenharmony_ci cep->qp = NULL; 150962306a36Sopenharmony_ci 151062306a36Sopenharmony_ci cep->cm_id = NULL; 151162306a36Sopenharmony_ci id->rem_ref(id); 151262306a36Sopenharmony_ci 151362306a36Sopenharmony_ci qp->cep = NULL; 151462306a36Sopenharmony_ci siw_cep_put(cep); 151562306a36Sopenharmony_ci 151662306a36Sopenharmony_ci cep->state = SIW_EPSTATE_CLOSED; 151762306a36Sopenharmony_ci 151862306a36Sopenharmony_ci siw_cep_set_free(cep); 151962306a36Sopenharmony_ci 152062306a36Sopenharmony_ci siw_cep_put(cep); 152162306a36Sopenharmony_ci 152262306a36Sopenharmony_ci } else if (s) { 152362306a36Sopenharmony_ci sock_release(s); 152462306a36Sopenharmony_ci } 152562306a36Sopenharmony_ci if (qp) 152662306a36Sopenharmony_ci siw_qp_put(qp); 152762306a36Sopenharmony_ci 152862306a36Sopenharmony_ci return rv; 152962306a36Sopenharmony_ci} 153062306a36Sopenharmony_ci 153162306a36Sopenharmony_ci/* 153262306a36Sopenharmony_ci * siw_accept - Let SoftiWARP accept an RDMA connection request 153362306a36Sopenharmony_ci * 153462306a36Sopenharmony_ci * @id: New connection management id to be used for accepted 153562306a36Sopenharmony_ci * connection request 153662306a36Sopenharmony_ci * @params: Connection parameters provided by ULP for accepting connection 153762306a36Sopenharmony_ci * 153862306a36Sopenharmony_ci * Transition QP to RTS state, associate new CM id @id with accepted CEP 153962306a36Sopenharmony_ci * and get prepared for TCP input by installing socket callbacks. 154062306a36Sopenharmony_ci * Then send MPA Reply and generate the "connection established" event. 154162306a36Sopenharmony_ci * Socket callbacks must be installed before sending MPA Reply, because 154262306a36Sopenharmony_ci * the latter may cause a first RDMA message to arrive from the RDMA Initiator 154362306a36Sopenharmony_ci * side very quickly, at which time the socket callbacks must be ready. 154462306a36Sopenharmony_ci */ 154562306a36Sopenharmony_ciint siw_accept(struct iw_cm_id *id, struct iw_cm_conn_param *params) 154662306a36Sopenharmony_ci{ 154762306a36Sopenharmony_ci struct siw_device *sdev = to_siw_dev(id->device); 154862306a36Sopenharmony_ci struct siw_cep *cep = (struct siw_cep *)id->provider_data; 154962306a36Sopenharmony_ci struct siw_qp *qp; 155062306a36Sopenharmony_ci struct siw_qp_attrs qp_attrs; 155162306a36Sopenharmony_ci int rv, max_priv_data = MPA_MAX_PRIVDATA; 155262306a36Sopenharmony_ci bool wait_for_peer_rts = false; 155362306a36Sopenharmony_ci 155462306a36Sopenharmony_ci siw_cep_set_inuse(cep); 155562306a36Sopenharmony_ci siw_cep_put(cep); 155662306a36Sopenharmony_ci 155762306a36Sopenharmony_ci /* Free lingering inbound private data */ 155862306a36Sopenharmony_ci if (cep->mpa.hdr.params.pd_len) { 155962306a36Sopenharmony_ci cep->mpa.hdr.params.pd_len = 0; 156062306a36Sopenharmony_ci kfree(cep->mpa.pdata); 156162306a36Sopenharmony_ci cep->mpa.pdata = NULL; 156262306a36Sopenharmony_ci } 156362306a36Sopenharmony_ci siw_cancel_mpatimer(cep); 156462306a36Sopenharmony_ci 156562306a36Sopenharmony_ci if (cep->state != SIW_EPSTATE_RECVD_MPAREQ) { 156662306a36Sopenharmony_ci siw_dbg_cep(cep, "out of state\n"); 156762306a36Sopenharmony_ci 156862306a36Sopenharmony_ci siw_cep_set_free(cep); 156962306a36Sopenharmony_ci siw_cep_put(cep); 157062306a36Sopenharmony_ci 157162306a36Sopenharmony_ci return -ECONNRESET; 157262306a36Sopenharmony_ci } 157362306a36Sopenharmony_ci qp = siw_qp_id2obj(sdev, params->qpn); 157462306a36Sopenharmony_ci if (!qp) { 157562306a36Sopenharmony_ci WARN(1, "[QP %d] does not exist\n", params->qpn); 157662306a36Sopenharmony_ci siw_cep_set_free(cep); 157762306a36Sopenharmony_ci siw_cep_put(cep); 157862306a36Sopenharmony_ci 157962306a36Sopenharmony_ci return -EINVAL; 158062306a36Sopenharmony_ci } 158162306a36Sopenharmony_ci down_write(&qp->state_lock); 158262306a36Sopenharmony_ci if (qp->attrs.state > SIW_QP_STATE_RTR) { 158362306a36Sopenharmony_ci rv = -EINVAL; 158462306a36Sopenharmony_ci up_write(&qp->state_lock); 158562306a36Sopenharmony_ci goto error; 158662306a36Sopenharmony_ci } 158762306a36Sopenharmony_ci siw_dbg_cep(cep, "[QP %d]\n", params->qpn); 158862306a36Sopenharmony_ci 158962306a36Sopenharmony_ci if (try_gso && cep->mpa.hdr.params.bits & MPA_RR_FLAG_GSO_EXP) { 159062306a36Sopenharmony_ci siw_dbg_cep(cep, "peer allows GSO on TX\n"); 159162306a36Sopenharmony_ci qp->tx_ctx.gso_seg_limit = 0; 159262306a36Sopenharmony_ci } 159362306a36Sopenharmony_ci if (params->ord > sdev->attrs.max_ord || 159462306a36Sopenharmony_ci params->ird > sdev->attrs.max_ird) { 159562306a36Sopenharmony_ci siw_dbg_cep( 159662306a36Sopenharmony_ci cep, 159762306a36Sopenharmony_ci "[QP %u]: ord %d (max %d), ird %d (max %d)\n", 159862306a36Sopenharmony_ci qp_id(qp), params->ord, sdev->attrs.max_ord, 159962306a36Sopenharmony_ci params->ird, sdev->attrs.max_ird); 160062306a36Sopenharmony_ci rv = -EINVAL; 160162306a36Sopenharmony_ci up_write(&qp->state_lock); 160262306a36Sopenharmony_ci goto error; 160362306a36Sopenharmony_ci } 160462306a36Sopenharmony_ci if (cep->enhanced_rdma_conn_est) 160562306a36Sopenharmony_ci max_priv_data -= sizeof(struct mpa_v2_data); 160662306a36Sopenharmony_ci 160762306a36Sopenharmony_ci if (params->private_data_len > max_priv_data) { 160862306a36Sopenharmony_ci siw_dbg_cep( 160962306a36Sopenharmony_ci cep, 161062306a36Sopenharmony_ci "[QP %u]: private data length: %d (max %d)\n", 161162306a36Sopenharmony_ci qp_id(qp), params->private_data_len, max_priv_data); 161262306a36Sopenharmony_ci rv = -EINVAL; 161362306a36Sopenharmony_ci up_write(&qp->state_lock); 161462306a36Sopenharmony_ci goto error; 161562306a36Sopenharmony_ci } 161662306a36Sopenharmony_ci if (cep->enhanced_rdma_conn_est) { 161762306a36Sopenharmony_ci if (params->ord > cep->ord) { 161862306a36Sopenharmony_ci if (relaxed_ird_negotiation) { 161962306a36Sopenharmony_ci params->ord = cep->ord; 162062306a36Sopenharmony_ci } else { 162162306a36Sopenharmony_ci cep->ird = params->ird; 162262306a36Sopenharmony_ci cep->ord = params->ord; 162362306a36Sopenharmony_ci rv = -EINVAL; 162462306a36Sopenharmony_ci up_write(&qp->state_lock); 162562306a36Sopenharmony_ci goto error; 162662306a36Sopenharmony_ci } 162762306a36Sopenharmony_ci } 162862306a36Sopenharmony_ci if (params->ird < cep->ird) { 162962306a36Sopenharmony_ci if (relaxed_ird_negotiation && 163062306a36Sopenharmony_ci cep->ird <= sdev->attrs.max_ird) 163162306a36Sopenharmony_ci params->ird = cep->ird; 163262306a36Sopenharmony_ci else { 163362306a36Sopenharmony_ci rv = -ENOMEM; 163462306a36Sopenharmony_ci up_write(&qp->state_lock); 163562306a36Sopenharmony_ci goto error; 163662306a36Sopenharmony_ci } 163762306a36Sopenharmony_ci } 163862306a36Sopenharmony_ci if (cep->mpa.v2_ctrl.ord & 163962306a36Sopenharmony_ci (MPA_V2_RDMA_WRITE_RTR | MPA_V2_RDMA_READ_RTR)) 164062306a36Sopenharmony_ci wait_for_peer_rts = true; 164162306a36Sopenharmony_ci /* 164262306a36Sopenharmony_ci * Signal back negotiated IRD and ORD values 164362306a36Sopenharmony_ci */ 164462306a36Sopenharmony_ci cep->mpa.v2_ctrl.ord = 164562306a36Sopenharmony_ci htons(params->ord & MPA_IRD_ORD_MASK) | 164662306a36Sopenharmony_ci (cep->mpa.v2_ctrl.ord & ~MPA_V2_MASK_IRD_ORD); 164762306a36Sopenharmony_ci cep->mpa.v2_ctrl.ird = 164862306a36Sopenharmony_ci htons(params->ird & MPA_IRD_ORD_MASK) | 164962306a36Sopenharmony_ci (cep->mpa.v2_ctrl.ird & ~MPA_V2_MASK_IRD_ORD); 165062306a36Sopenharmony_ci } 165162306a36Sopenharmony_ci cep->ird = params->ird; 165262306a36Sopenharmony_ci cep->ord = params->ord; 165362306a36Sopenharmony_ci 165462306a36Sopenharmony_ci cep->cm_id = id; 165562306a36Sopenharmony_ci id->add_ref(id); 165662306a36Sopenharmony_ci 165762306a36Sopenharmony_ci memset(&qp_attrs, 0, sizeof(qp_attrs)); 165862306a36Sopenharmony_ci qp_attrs.orq_size = cep->ord; 165962306a36Sopenharmony_ci qp_attrs.irq_size = cep->ird; 166062306a36Sopenharmony_ci qp_attrs.sk = cep->sock; 166162306a36Sopenharmony_ci if (cep->mpa.hdr.params.bits & MPA_RR_FLAG_CRC) 166262306a36Sopenharmony_ci qp_attrs.flags = SIW_MPA_CRC; 166362306a36Sopenharmony_ci qp_attrs.state = SIW_QP_STATE_RTS; 166462306a36Sopenharmony_ci 166562306a36Sopenharmony_ci siw_dbg_cep(cep, "[QP%u]: moving to rts\n", qp_id(qp)); 166662306a36Sopenharmony_ci 166762306a36Sopenharmony_ci /* Associate QP with CEP */ 166862306a36Sopenharmony_ci siw_cep_get(cep); 166962306a36Sopenharmony_ci qp->cep = cep; 167062306a36Sopenharmony_ci 167162306a36Sopenharmony_ci /* siw_qp_get(qp) already done by QP lookup */ 167262306a36Sopenharmony_ci cep->qp = qp; 167362306a36Sopenharmony_ci 167462306a36Sopenharmony_ci cep->state = SIW_EPSTATE_RDMA_MODE; 167562306a36Sopenharmony_ci 167662306a36Sopenharmony_ci /* Move socket RX/TX under QP control */ 167762306a36Sopenharmony_ci rv = siw_qp_modify(qp, &qp_attrs, 167862306a36Sopenharmony_ci SIW_QP_ATTR_STATE | SIW_QP_ATTR_LLP_HANDLE | 167962306a36Sopenharmony_ci SIW_QP_ATTR_ORD | SIW_QP_ATTR_IRD | 168062306a36Sopenharmony_ci SIW_QP_ATTR_MPA); 168162306a36Sopenharmony_ci up_write(&qp->state_lock); 168262306a36Sopenharmony_ci 168362306a36Sopenharmony_ci if (rv) 168462306a36Sopenharmony_ci goto error; 168562306a36Sopenharmony_ci 168662306a36Sopenharmony_ci siw_dbg_cep(cep, "[QP %u]: send mpa reply, %d byte pdata\n", 168762306a36Sopenharmony_ci qp_id(qp), params->private_data_len); 168862306a36Sopenharmony_ci 168962306a36Sopenharmony_ci rv = siw_send_mpareqrep(cep, params->private_data, 169062306a36Sopenharmony_ci params->private_data_len); 169162306a36Sopenharmony_ci if (rv != 0) 169262306a36Sopenharmony_ci goto error; 169362306a36Sopenharmony_ci 169462306a36Sopenharmony_ci if (wait_for_peer_rts) { 169562306a36Sopenharmony_ci siw_sk_assign_rtr_upcalls(cep); 169662306a36Sopenharmony_ci } else { 169762306a36Sopenharmony_ci siw_qp_socket_assoc(cep, qp); 169862306a36Sopenharmony_ci rv = siw_cm_upcall(cep, IW_CM_EVENT_ESTABLISHED, 0); 169962306a36Sopenharmony_ci if (rv) 170062306a36Sopenharmony_ci goto error; 170162306a36Sopenharmony_ci } 170262306a36Sopenharmony_ci siw_cep_set_free(cep); 170362306a36Sopenharmony_ci 170462306a36Sopenharmony_ci return 0; 170562306a36Sopenharmony_cierror: 170662306a36Sopenharmony_ci siw_socket_disassoc(cep->sock); 170762306a36Sopenharmony_ci sock_release(cep->sock); 170862306a36Sopenharmony_ci cep->sock = NULL; 170962306a36Sopenharmony_ci 171062306a36Sopenharmony_ci cep->state = SIW_EPSTATE_CLOSED; 171162306a36Sopenharmony_ci 171262306a36Sopenharmony_ci if (cep->cm_id) { 171362306a36Sopenharmony_ci cep->cm_id->rem_ref(id); 171462306a36Sopenharmony_ci cep->cm_id = NULL; 171562306a36Sopenharmony_ci } 171662306a36Sopenharmony_ci if (qp->cep) { 171762306a36Sopenharmony_ci siw_cep_put(cep); 171862306a36Sopenharmony_ci qp->cep = NULL; 171962306a36Sopenharmony_ci } 172062306a36Sopenharmony_ci cep->qp = NULL; 172162306a36Sopenharmony_ci siw_qp_put(qp); 172262306a36Sopenharmony_ci 172362306a36Sopenharmony_ci siw_cep_set_free(cep); 172462306a36Sopenharmony_ci siw_cep_put(cep); 172562306a36Sopenharmony_ci 172662306a36Sopenharmony_ci return rv; 172762306a36Sopenharmony_ci} 172862306a36Sopenharmony_ci 172962306a36Sopenharmony_ci/* 173062306a36Sopenharmony_ci * siw_reject() 173162306a36Sopenharmony_ci * 173262306a36Sopenharmony_ci * Local connection reject case. Send private data back to peer, 173362306a36Sopenharmony_ci * close connection and dereference connection id. 173462306a36Sopenharmony_ci */ 173562306a36Sopenharmony_ciint siw_reject(struct iw_cm_id *id, const void *pdata, u8 pd_len) 173662306a36Sopenharmony_ci{ 173762306a36Sopenharmony_ci struct siw_cep *cep = (struct siw_cep *)id->provider_data; 173862306a36Sopenharmony_ci 173962306a36Sopenharmony_ci siw_cep_set_inuse(cep); 174062306a36Sopenharmony_ci siw_cep_put(cep); 174162306a36Sopenharmony_ci 174262306a36Sopenharmony_ci siw_cancel_mpatimer(cep); 174362306a36Sopenharmony_ci 174462306a36Sopenharmony_ci if (cep->state != SIW_EPSTATE_RECVD_MPAREQ) { 174562306a36Sopenharmony_ci siw_dbg_cep(cep, "out of state\n"); 174662306a36Sopenharmony_ci 174762306a36Sopenharmony_ci siw_cep_set_free(cep); 174862306a36Sopenharmony_ci siw_cep_put(cep); /* put last reference */ 174962306a36Sopenharmony_ci 175062306a36Sopenharmony_ci return -ECONNRESET; 175162306a36Sopenharmony_ci } 175262306a36Sopenharmony_ci siw_dbg_cep(cep, "cep->state %d, pd_len %d\n", cep->state, 175362306a36Sopenharmony_ci pd_len); 175462306a36Sopenharmony_ci 175562306a36Sopenharmony_ci if (__mpa_rr_revision(cep->mpa.hdr.params.bits) >= MPA_REVISION_1) { 175662306a36Sopenharmony_ci cep->mpa.hdr.params.bits |= MPA_RR_FLAG_REJECT; /* reject */ 175762306a36Sopenharmony_ci siw_send_mpareqrep(cep, pdata, pd_len); 175862306a36Sopenharmony_ci } 175962306a36Sopenharmony_ci siw_socket_disassoc(cep->sock); 176062306a36Sopenharmony_ci sock_release(cep->sock); 176162306a36Sopenharmony_ci cep->sock = NULL; 176262306a36Sopenharmony_ci 176362306a36Sopenharmony_ci cep->state = SIW_EPSTATE_CLOSED; 176462306a36Sopenharmony_ci 176562306a36Sopenharmony_ci siw_cep_set_free(cep); 176662306a36Sopenharmony_ci siw_cep_put(cep); 176762306a36Sopenharmony_ci 176862306a36Sopenharmony_ci return 0; 176962306a36Sopenharmony_ci} 177062306a36Sopenharmony_ci 177162306a36Sopenharmony_ci/* 177262306a36Sopenharmony_ci * siw_create_listen - Create resources for a listener's IWCM ID @id 177362306a36Sopenharmony_ci * 177462306a36Sopenharmony_ci * Starts listen on the socket address id->local_addr. 177562306a36Sopenharmony_ci * 177662306a36Sopenharmony_ci */ 177762306a36Sopenharmony_ciint siw_create_listen(struct iw_cm_id *id, int backlog) 177862306a36Sopenharmony_ci{ 177962306a36Sopenharmony_ci struct socket *s; 178062306a36Sopenharmony_ci struct siw_cep *cep = NULL; 178162306a36Sopenharmony_ci struct siw_device *sdev = to_siw_dev(id->device); 178262306a36Sopenharmony_ci int addr_family = id->local_addr.ss_family; 178362306a36Sopenharmony_ci int rv = 0; 178462306a36Sopenharmony_ci 178562306a36Sopenharmony_ci if (addr_family != AF_INET && addr_family != AF_INET6) 178662306a36Sopenharmony_ci return -EAFNOSUPPORT; 178762306a36Sopenharmony_ci 178862306a36Sopenharmony_ci rv = sock_create(addr_family, SOCK_STREAM, IPPROTO_TCP, &s); 178962306a36Sopenharmony_ci if (rv < 0) 179062306a36Sopenharmony_ci return rv; 179162306a36Sopenharmony_ci 179262306a36Sopenharmony_ci /* 179362306a36Sopenharmony_ci * Allow binding local port when still in TIME_WAIT from last close. 179462306a36Sopenharmony_ci */ 179562306a36Sopenharmony_ci sock_set_reuseaddr(s->sk); 179662306a36Sopenharmony_ci 179762306a36Sopenharmony_ci if (addr_family == AF_INET) { 179862306a36Sopenharmony_ci struct sockaddr_in *laddr = &to_sockaddr_in(id->local_addr); 179962306a36Sopenharmony_ci 180062306a36Sopenharmony_ci /* For wildcard addr, limit binding to current device only */ 180162306a36Sopenharmony_ci if (ipv4_is_zeronet(laddr->sin_addr.s_addr)) 180262306a36Sopenharmony_ci s->sk->sk_bound_dev_if = sdev->netdev->ifindex; 180362306a36Sopenharmony_ci 180462306a36Sopenharmony_ci rv = s->ops->bind(s, (struct sockaddr *)laddr, 180562306a36Sopenharmony_ci sizeof(struct sockaddr_in)); 180662306a36Sopenharmony_ci } else { 180762306a36Sopenharmony_ci struct sockaddr_in6 *laddr = &to_sockaddr_in6(id->local_addr); 180862306a36Sopenharmony_ci 180962306a36Sopenharmony_ci if (id->afonly) { 181062306a36Sopenharmony_ci rv = ip6_sock_set_v6only(s->sk); 181162306a36Sopenharmony_ci if (rv) { 181262306a36Sopenharmony_ci siw_dbg(id->device, 181362306a36Sopenharmony_ci "ip6_sock_set_v6only erro: %d\n", rv); 181462306a36Sopenharmony_ci goto error; 181562306a36Sopenharmony_ci } 181662306a36Sopenharmony_ci } 181762306a36Sopenharmony_ci 181862306a36Sopenharmony_ci /* For wildcard addr, limit binding to current device only */ 181962306a36Sopenharmony_ci if (ipv6_addr_any(&laddr->sin6_addr)) 182062306a36Sopenharmony_ci s->sk->sk_bound_dev_if = sdev->netdev->ifindex; 182162306a36Sopenharmony_ci 182262306a36Sopenharmony_ci rv = s->ops->bind(s, (struct sockaddr *)laddr, 182362306a36Sopenharmony_ci sizeof(struct sockaddr_in6)); 182462306a36Sopenharmony_ci } 182562306a36Sopenharmony_ci if (rv) { 182662306a36Sopenharmony_ci siw_dbg(id->device, "socket bind error: %d\n", rv); 182762306a36Sopenharmony_ci goto error; 182862306a36Sopenharmony_ci } 182962306a36Sopenharmony_ci cep = siw_cep_alloc(sdev); 183062306a36Sopenharmony_ci if (!cep) { 183162306a36Sopenharmony_ci rv = -ENOMEM; 183262306a36Sopenharmony_ci goto error; 183362306a36Sopenharmony_ci } 183462306a36Sopenharmony_ci siw_cep_socket_assoc(cep, s); 183562306a36Sopenharmony_ci 183662306a36Sopenharmony_ci rv = siw_cm_alloc_work(cep, backlog); 183762306a36Sopenharmony_ci if (rv) { 183862306a36Sopenharmony_ci siw_dbg(id->device, 183962306a36Sopenharmony_ci "alloc_work error %d, backlog %d\n", 184062306a36Sopenharmony_ci rv, backlog); 184162306a36Sopenharmony_ci goto error; 184262306a36Sopenharmony_ci } 184362306a36Sopenharmony_ci rv = s->ops->listen(s, backlog); 184462306a36Sopenharmony_ci if (rv) { 184562306a36Sopenharmony_ci siw_dbg(id->device, "listen error %d\n", rv); 184662306a36Sopenharmony_ci goto error; 184762306a36Sopenharmony_ci } 184862306a36Sopenharmony_ci cep->cm_id = id; 184962306a36Sopenharmony_ci id->add_ref(id); 185062306a36Sopenharmony_ci 185162306a36Sopenharmony_ci /* 185262306a36Sopenharmony_ci * In case of a wildcard rdma_listen on a multi-homed device, 185362306a36Sopenharmony_ci * a listener's IWCM id is associated with more than one listening CEP. 185462306a36Sopenharmony_ci * 185562306a36Sopenharmony_ci * We currently use id->provider_data in three different ways: 185662306a36Sopenharmony_ci * 185762306a36Sopenharmony_ci * o For a listener's IWCM id, id->provider_data points to 185862306a36Sopenharmony_ci * the list_head of the list of listening CEPs. 185962306a36Sopenharmony_ci * Uses: siw_create_listen(), siw_destroy_listen() 186062306a36Sopenharmony_ci * 186162306a36Sopenharmony_ci * o For each accepted passive-side IWCM id, id->provider_data 186262306a36Sopenharmony_ci * points to the CEP itself. This is a consequence of 186362306a36Sopenharmony_ci * - siw_cm_upcall() setting event.provider_data = cep and 186462306a36Sopenharmony_ci * - the IWCM's cm_conn_req_handler() setting provider_data of the 186562306a36Sopenharmony_ci * new passive-side IWCM id equal to event.provider_data 186662306a36Sopenharmony_ci * Uses: siw_accept(), siw_reject() 186762306a36Sopenharmony_ci * 186862306a36Sopenharmony_ci * o For an active-side IWCM id, id->provider_data is not used at all. 186962306a36Sopenharmony_ci * 187062306a36Sopenharmony_ci */ 187162306a36Sopenharmony_ci if (!id->provider_data) { 187262306a36Sopenharmony_ci id->provider_data = 187362306a36Sopenharmony_ci kmalloc(sizeof(struct list_head), GFP_KERNEL); 187462306a36Sopenharmony_ci if (!id->provider_data) { 187562306a36Sopenharmony_ci rv = -ENOMEM; 187662306a36Sopenharmony_ci goto error; 187762306a36Sopenharmony_ci } 187862306a36Sopenharmony_ci INIT_LIST_HEAD((struct list_head *)id->provider_data); 187962306a36Sopenharmony_ci } 188062306a36Sopenharmony_ci list_add_tail(&cep->listenq, (struct list_head *)id->provider_data); 188162306a36Sopenharmony_ci cep->state = SIW_EPSTATE_LISTENING; 188262306a36Sopenharmony_ci 188362306a36Sopenharmony_ci siw_dbg(id->device, "Listen at laddr %pISp\n", &id->local_addr); 188462306a36Sopenharmony_ci 188562306a36Sopenharmony_ci return 0; 188662306a36Sopenharmony_ci 188762306a36Sopenharmony_cierror: 188862306a36Sopenharmony_ci siw_dbg(id->device, "failed: %d\n", rv); 188962306a36Sopenharmony_ci 189062306a36Sopenharmony_ci if (cep) { 189162306a36Sopenharmony_ci siw_cep_set_inuse(cep); 189262306a36Sopenharmony_ci 189362306a36Sopenharmony_ci if (cep->cm_id) { 189462306a36Sopenharmony_ci cep->cm_id->rem_ref(cep->cm_id); 189562306a36Sopenharmony_ci cep->cm_id = NULL; 189662306a36Sopenharmony_ci } 189762306a36Sopenharmony_ci cep->sock = NULL; 189862306a36Sopenharmony_ci siw_socket_disassoc(s); 189962306a36Sopenharmony_ci cep->state = SIW_EPSTATE_CLOSED; 190062306a36Sopenharmony_ci 190162306a36Sopenharmony_ci siw_cep_set_free(cep); 190262306a36Sopenharmony_ci siw_cep_put(cep); 190362306a36Sopenharmony_ci } 190462306a36Sopenharmony_ci sock_release(s); 190562306a36Sopenharmony_ci 190662306a36Sopenharmony_ci return rv; 190762306a36Sopenharmony_ci} 190862306a36Sopenharmony_ci 190962306a36Sopenharmony_cistatic void siw_drop_listeners(struct iw_cm_id *id) 191062306a36Sopenharmony_ci{ 191162306a36Sopenharmony_ci struct list_head *p, *tmp; 191262306a36Sopenharmony_ci 191362306a36Sopenharmony_ci /* 191462306a36Sopenharmony_ci * In case of a wildcard rdma_listen on a multi-homed device, 191562306a36Sopenharmony_ci * a listener's IWCM id is associated with more than one listening CEP. 191662306a36Sopenharmony_ci */ 191762306a36Sopenharmony_ci list_for_each_safe(p, tmp, (struct list_head *)id->provider_data) { 191862306a36Sopenharmony_ci struct siw_cep *cep = list_entry(p, struct siw_cep, listenq); 191962306a36Sopenharmony_ci 192062306a36Sopenharmony_ci list_del(p); 192162306a36Sopenharmony_ci 192262306a36Sopenharmony_ci siw_dbg_cep(cep, "drop cep, state %d\n", cep->state); 192362306a36Sopenharmony_ci 192462306a36Sopenharmony_ci siw_cep_set_inuse(cep); 192562306a36Sopenharmony_ci 192662306a36Sopenharmony_ci if (cep->cm_id) { 192762306a36Sopenharmony_ci cep->cm_id->rem_ref(cep->cm_id); 192862306a36Sopenharmony_ci cep->cm_id = NULL; 192962306a36Sopenharmony_ci } 193062306a36Sopenharmony_ci if (cep->sock) { 193162306a36Sopenharmony_ci siw_socket_disassoc(cep->sock); 193262306a36Sopenharmony_ci sock_release(cep->sock); 193362306a36Sopenharmony_ci cep->sock = NULL; 193462306a36Sopenharmony_ci } 193562306a36Sopenharmony_ci cep->state = SIW_EPSTATE_CLOSED; 193662306a36Sopenharmony_ci siw_cep_set_free(cep); 193762306a36Sopenharmony_ci siw_cep_put(cep); 193862306a36Sopenharmony_ci } 193962306a36Sopenharmony_ci} 194062306a36Sopenharmony_ci 194162306a36Sopenharmony_ciint siw_destroy_listen(struct iw_cm_id *id) 194262306a36Sopenharmony_ci{ 194362306a36Sopenharmony_ci if (!id->provider_data) { 194462306a36Sopenharmony_ci siw_dbg(id->device, "no cep(s)\n"); 194562306a36Sopenharmony_ci return 0; 194662306a36Sopenharmony_ci } 194762306a36Sopenharmony_ci siw_drop_listeners(id); 194862306a36Sopenharmony_ci kfree(id->provider_data); 194962306a36Sopenharmony_ci id->provider_data = NULL; 195062306a36Sopenharmony_ci 195162306a36Sopenharmony_ci return 0; 195262306a36Sopenharmony_ci} 195362306a36Sopenharmony_ci 195462306a36Sopenharmony_ciint siw_cm_init(void) 195562306a36Sopenharmony_ci{ 195662306a36Sopenharmony_ci /* 195762306a36Sopenharmony_ci * create_single_workqueue for strict ordering 195862306a36Sopenharmony_ci */ 195962306a36Sopenharmony_ci siw_cm_wq = create_singlethread_workqueue("siw_cm_wq"); 196062306a36Sopenharmony_ci if (!siw_cm_wq) 196162306a36Sopenharmony_ci return -ENOMEM; 196262306a36Sopenharmony_ci 196362306a36Sopenharmony_ci return 0; 196462306a36Sopenharmony_ci} 196562306a36Sopenharmony_ci 196662306a36Sopenharmony_civoid siw_cm_exit(void) 196762306a36Sopenharmony_ci{ 196862306a36Sopenharmony_ci if (siw_cm_wq) 196962306a36Sopenharmony_ci destroy_workqueue(siw_cm_wq); 197062306a36Sopenharmony_ci} 1971