162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright(c) 2018 Intel Corporation. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci#include "hfi.h" 762306a36Sopenharmony_ci#include "trace.h" 862306a36Sopenharmony_ci#include "qp.h" 962306a36Sopenharmony_ci#include "opfn.h" 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#define IB_BTHE_E BIT(IB_BTHE_E_SHIFT) 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#define OPFN_CODE(code) BIT((code) - 1) 1462306a36Sopenharmony_ci#define OPFN_MASK(code) OPFN_CODE(STL_VERBS_EXTD_##code) 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_cistruct hfi1_opfn_type { 1762306a36Sopenharmony_ci bool (*request)(struct rvt_qp *qp, u64 *data); 1862306a36Sopenharmony_ci bool (*response)(struct rvt_qp *qp, u64 *data); 1962306a36Sopenharmony_ci bool (*reply)(struct rvt_qp *qp, u64 data); 2062306a36Sopenharmony_ci void (*error)(struct rvt_qp *qp); 2162306a36Sopenharmony_ci}; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic struct hfi1_opfn_type hfi1_opfn_handlers[STL_VERBS_EXTD_MAX] = { 2462306a36Sopenharmony_ci [STL_VERBS_EXTD_TID_RDMA] = { 2562306a36Sopenharmony_ci .request = tid_rdma_conn_req, 2662306a36Sopenharmony_ci .response = tid_rdma_conn_resp, 2762306a36Sopenharmony_ci .reply = tid_rdma_conn_reply, 2862306a36Sopenharmony_ci .error = tid_rdma_conn_error, 2962306a36Sopenharmony_ci }, 3062306a36Sopenharmony_ci}; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic struct workqueue_struct *opfn_wq; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistatic void opfn_schedule_conn_request(struct rvt_qp *qp); 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistatic bool hfi1_opfn_extended(u32 bth1) 3762306a36Sopenharmony_ci{ 3862306a36Sopenharmony_ci return !!(bth1 & IB_BTHE_E); 3962306a36Sopenharmony_ci} 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic void opfn_conn_request(struct rvt_qp *qp) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci struct hfi1_qp_priv *priv = qp->priv; 4462306a36Sopenharmony_ci struct ib_atomic_wr wr; 4562306a36Sopenharmony_ci u16 mask, capcode; 4662306a36Sopenharmony_ci struct hfi1_opfn_type *extd; 4762306a36Sopenharmony_ci u64 data; 4862306a36Sopenharmony_ci unsigned long flags; 4962306a36Sopenharmony_ci int ret = 0; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci trace_hfi1_opfn_state_conn_request(qp); 5262306a36Sopenharmony_ci spin_lock_irqsave(&priv->opfn.lock, flags); 5362306a36Sopenharmony_ci /* 5462306a36Sopenharmony_ci * Exit if the extended bit is not set, or if nothing is requested, or 5562306a36Sopenharmony_ci * if we have completed all requests, or if a previous request is in 5662306a36Sopenharmony_ci * progress 5762306a36Sopenharmony_ci */ 5862306a36Sopenharmony_ci if (!priv->opfn.extended || !priv->opfn.requested || 5962306a36Sopenharmony_ci priv->opfn.requested == priv->opfn.completed || priv->opfn.curr) 6062306a36Sopenharmony_ci goto done; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci mask = priv->opfn.requested & ~priv->opfn.completed; 6362306a36Sopenharmony_ci capcode = ilog2(mask & ~(mask - 1)) + 1; 6462306a36Sopenharmony_ci if (capcode >= STL_VERBS_EXTD_MAX) { 6562306a36Sopenharmony_ci priv->opfn.completed |= OPFN_CODE(capcode); 6662306a36Sopenharmony_ci goto done; 6762306a36Sopenharmony_ci } 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci extd = &hfi1_opfn_handlers[capcode]; 7062306a36Sopenharmony_ci if (!extd || !extd->request || !extd->request(qp, &data)) { 7162306a36Sopenharmony_ci /* 7262306a36Sopenharmony_ci * Either there is no handler for this capability or the request 7362306a36Sopenharmony_ci * packet could not be generated. Either way, mark it as done so 7462306a36Sopenharmony_ci * we don't keep attempting to complete it. 7562306a36Sopenharmony_ci */ 7662306a36Sopenharmony_ci priv->opfn.completed |= OPFN_CODE(capcode); 7762306a36Sopenharmony_ci goto done; 7862306a36Sopenharmony_ci } 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci trace_hfi1_opfn_data_conn_request(qp, capcode, data); 8162306a36Sopenharmony_ci data = (data & ~0xf) | capcode; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci memset(&wr, 0, sizeof(wr)); 8462306a36Sopenharmony_ci wr.wr.opcode = IB_WR_OPFN; 8562306a36Sopenharmony_ci wr.remote_addr = HFI1_VERBS_E_ATOMIC_VADDR; 8662306a36Sopenharmony_ci wr.compare_add = data; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci priv->opfn.curr = capcode; /* A new request is now in progress */ 8962306a36Sopenharmony_ci /* Drop opfn.lock before calling ib_post_send() */ 9062306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->opfn.lock, flags); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci ret = ib_post_send(&qp->ibqp, &wr.wr, NULL); 9362306a36Sopenharmony_ci if (ret) 9462306a36Sopenharmony_ci goto err; 9562306a36Sopenharmony_ci trace_hfi1_opfn_state_conn_request(qp); 9662306a36Sopenharmony_ci return; 9762306a36Sopenharmony_cierr: 9862306a36Sopenharmony_ci trace_hfi1_msg_opfn_conn_request(qp, "ib_ost_send failed: ret = ", 9962306a36Sopenharmony_ci (u64)ret); 10062306a36Sopenharmony_ci spin_lock_irqsave(&priv->opfn.lock, flags); 10162306a36Sopenharmony_ci /* 10262306a36Sopenharmony_ci * In case of an unexpected error return from ib_post_send 10362306a36Sopenharmony_ci * clear opfn.curr and reschedule to try again 10462306a36Sopenharmony_ci */ 10562306a36Sopenharmony_ci priv->opfn.curr = STL_VERBS_EXTD_NONE; 10662306a36Sopenharmony_ci opfn_schedule_conn_request(qp); 10762306a36Sopenharmony_cidone: 10862306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->opfn.lock, flags); 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_civoid opfn_send_conn_request(struct work_struct *work) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci struct hfi1_opfn_data *od; 11462306a36Sopenharmony_ci struct hfi1_qp_priv *qpriv; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci od = container_of(work, struct hfi1_opfn_data, opfn_work); 11762306a36Sopenharmony_ci qpriv = container_of(od, struct hfi1_qp_priv, opfn); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci opfn_conn_request(qpriv->owner); 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci/* 12362306a36Sopenharmony_ci * When QP s_lock is held in the caller, the OPFN request must be scheduled 12462306a36Sopenharmony_ci * to a different workqueue to avoid double locking QP s_lock in call to 12562306a36Sopenharmony_ci * ib_post_send in opfn_conn_request 12662306a36Sopenharmony_ci */ 12762306a36Sopenharmony_cistatic void opfn_schedule_conn_request(struct rvt_qp *qp) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci struct hfi1_qp_priv *priv = qp->priv; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci trace_hfi1_opfn_state_sched_conn_request(qp); 13262306a36Sopenharmony_ci queue_work(opfn_wq, &priv->opfn.opfn_work); 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_civoid opfn_conn_response(struct rvt_qp *qp, struct rvt_ack_entry *e, 13662306a36Sopenharmony_ci struct ib_atomic_eth *ateth) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci struct hfi1_qp_priv *priv = qp->priv; 13962306a36Sopenharmony_ci u64 data = be64_to_cpu(ateth->compare_data); 14062306a36Sopenharmony_ci struct hfi1_opfn_type *extd; 14162306a36Sopenharmony_ci u8 capcode; 14262306a36Sopenharmony_ci unsigned long flags; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci trace_hfi1_opfn_state_conn_response(qp); 14562306a36Sopenharmony_ci capcode = data & 0xf; 14662306a36Sopenharmony_ci trace_hfi1_opfn_data_conn_response(qp, capcode, data); 14762306a36Sopenharmony_ci if (!capcode || capcode >= STL_VERBS_EXTD_MAX) 14862306a36Sopenharmony_ci return; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci extd = &hfi1_opfn_handlers[capcode]; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci if (!extd || !extd->response) { 15362306a36Sopenharmony_ci e->atomic_data = capcode; 15462306a36Sopenharmony_ci return; 15562306a36Sopenharmony_ci } 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci spin_lock_irqsave(&priv->opfn.lock, flags); 15862306a36Sopenharmony_ci if (priv->opfn.completed & OPFN_CODE(capcode)) { 15962306a36Sopenharmony_ci /* 16062306a36Sopenharmony_ci * We are receiving a request for a feature that has already 16162306a36Sopenharmony_ci * been negotiated. This may mean that the other side has reset 16262306a36Sopenharmony_ci */ 16362306a36Sopenharmony_ci priv->opfn.completed &= ~OPFN_CODE(capcode); 16462306a36Sopenharmony_ci if (extd->error) 16562306a36Sopenharmony_ci extd->error(qp); 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci if (extd->response(qp, &data)) 16962306a36Sopenharmony_ci priv->opfn.completed |= OPFN_CODE(capcode); 17062306a36Sopenharmony_ci e->atomic_data = (data & ~0xf) | capcode; 17162306a36Sopenharmony_ci trace_hfi1_opfn_state_conn_response(qp); 17262306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->opfn.lock, flags); 17362306a36Sopenharmony_ci} 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_civoid opfn_conn_reply(struct rvt_qp *qp, u64 data) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci struct hfi1_qp_priv *priv = qp->priv; 17862306a36Sopenharmony_ci struct hfi1_opfn_type *extd; 17962306a36Sopenharmony_ci u8 capcode; 18062306a36Sopenharmony_ci unsigned long flags; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci trace_hfi1_opfn_state_conn_reply(qp); 18362306a36Sopenharmony_ci capcode = data & 0xf; 18462306a36Sopenharmony_ci trace_hfi1_opfn_data_conn_reply(qp, capcode, data); 18562306a36Sopenharmony_ci if (!capcode || capcode >= STL_VERBS_EXTD_MAX) 18662306a36Sopenharmony_ci return; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci spin_lock_irqsave(&priv->opfn.lock, flags); 18962306a36Sopenharmony_ci /* 19062306a36Sopenharmony_ci * Either there is no previous request or the reply is not for the 19162306a36Sopenharmony_ci * current request 19262306a36Sopenharmony_ci */ 19362306a36Sopenharmony_ci if (!priv->opfn.curr || capcode != priv->opfn.curr) 19462306a36Sopenharmony_ci goto done; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci extd = &hfi1_opfn_handlers[capcode]; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci if (!extd || !extd->reply) 19962306a36Sopenharmony_ci goto clear; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci if (extd->reply(qp, data)) 20262306a36Sopenharmony_ci priv->opfn.completed |= OPFN_CODE(capcode); 20362306a36Sopenharmony_ciclear: 20462306a36Sopenharmony_ci /* 20562306a36Sopenharmony_ci * Clear opfn.curr to indicate that the previous request is no longer in 20662306a36Sopenharmony_ci * progress 20762306a36Sopenharmony_ci */ 20862306a36Sopenharmony_ci priv->opfn.curr = STL_VERBS_EXTD_NONE; 20962306a36Sopenharmony_ci trace_hfi1_opfn_state_conn_reply(qp); 21062306a36Sopenharmony_cidone: 21162306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->opfn.lock, flags); 21262306a36Sopenharmony_ci} 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_civoid opfn_conn_error(struct rvt_qp *qp) 21562306a36Sopenharmony_ci{ 21662306a36Sopenharmony_ci struct hfi1_qp_priv *priv = qp->priv; 21762306a36Sopenharmony_ci struct hfi1_opfn_type *extd = NULL; 21862306a36Sopenharmony_ci unsigned long flags; 21962306a36Sopenharmony_ci u16 capcode; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci trace_hfi1_opfn_state_conn_error(qp); 22262306a36Sopenharmony_ci trace_hfi1_msg_opfn_conn_error(qp, "error. qp state ", (u64)qp->state); 22362306a36Sopenharmony_ci /* 22462306a36Sopenharmony_ci * The QP has gone into the Error state. We have to invalidate all 22562306a36Sopenharmony_ci * negotiated feature, including the one in progress (if any). The RC 22662306a36Sopenharmony_ci * QP handling will clean the WQE for the connection request. 22762306a36Sopenharmony_ci */ 22862306a36Sopenharmony_ci spin_lock_irqsave(&priv->opfn.lock, flags); 22962306a36Sopenharmony_ci while (priv->opfn.completed) { 23062306a36Sopenharmony_ci capcode = priv->opfn.completed & ~(priv->opfn.completed - 1); 23162306a36Sopenharmony_ci extd = &hfi1_opfn_handlers[ilog2(capcode) + 1]; 23262306a36Sopenharmony_ci if (extd->error) 23362306a36Sopenharmony_ci extd->error(qp); 23462306a36Sopenharmony_ci priv->opfn.completed &= ~OPFN_CODE(capcode); 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci priv->opfn.extended = 0; 23762306a36Sopenharmony_ci priv->opfn.requested = 0; 23862306a36Sopenharmony_ci priv->opfn.curr = STL_VERBS_EXTD_NONE; 23962306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->opfn.lock, flags); 24062306a36Sopenharmony_ci} 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_civoid opfn_qp_init(struct rvt_qp *qp, struct ib_qp_attr *attr, int attr_mask) 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci struct ib_qp *ibqp = &qp->ibqp; 24562306a36Sopenharmony_ci struct hfi1_qp_priv *priv = qp->priv; 24662306a36Sopenharmony_ci unsigned long flags; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci if (attr_mask & IB_QP_RETRY_CNT) 24962306a36Sopenharmony_ci priv->s_retry = attr->retry_cnt; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci spin_lock_irqsave(&priv->opfn.lock, flags); 25262306a36Sopenharmony_ci if (ibqp->qp_type == IB_QPT_RC && HFI1_CAP_IS_KSET(TID_RDMA)) { 25362306a36Sopenharmony_ci struct tid_rdma_params *local = &priv->tid_rdma.local; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci if (attr_mask & IB_QP_TIMEOUT) 25662306a36Sopenharmony_ci priv->tid_retry_timeout_jiffies = qp->timeout_jiffies; 25762306a36Sopenharmony_ci if (qp->pmtu == enum_to_mtu(OPA_MTU_4096) || 25862306a36Sopenharmony_ci qp->pmtu == enum_to_mtu(OPA_MTU_8192)) { 25962306a36Sopenharmony_ci tid_rdma_opfn_init(qp, local); 26062306a36Sopenharmony_ci /* 26162306a36Sopenharmony_ci * We only want to set the OPFN requested bit when the 26262306a36Sopenharmony_ci * QP transitions to RTS. 26362306a36Sopenharmony_ci */ 26462306a36Sopenharmony_ci if (attr_mask & IB_QP_STATE && 26562306a36Sopenharmony_ci attr->qp_state == IB_QPS_RTS) { 26662306a36Sopenharmony_ci priv->opfn.requested |= OPFN_MASK(TID_RDMA); 26762306a36Sopenharmony_ci /* 26862306a36Sopenharmony_ci * If the QP is transitioning to RTS and the 26962306a36Sopenharmony_ci * opfn.completed for TID RDMA has already been 27062306a36Sopenharmony_ci * set, the QP is being moved *back* into RTS. 27162306a36Sopenharmony_ci * We can now renegotiate the TID RDMA 27262306a36Sopenharmony_ci * parameters. 27362306a36Sopenharmony_ci */ 27462306a36Sopenharmony_ci if (priv->opfn.completed & 27562306a36Sopenharmony_ci OPFN_MASK(TID_RDMA)) { 27662306a36Sopenharmony_ci priv->opfn.completed &= 27762306a36Sopenharmony_ci ~OPFN_MASK(TID_RDMA); 27862306a36Sopenharmony_ci /* 27962306a36Sopenharmony_ci * Since the opfn.completed bit was 28062306a36Sopenharmony_ci * already set, it is safe to assume 28162306a36Sopenharmony_ci * that the opfn.extended is also set. 28262306a36Sopenharmony_ci */ 28362306a36Sopenharmony_ci opfn_schedule_conn_request(qp); 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci } else { 28762306a36Sopenharmony_ci memset(local, 0, sizeof(*local)); 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->opfn.lock, flags); 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_civoid opfn_trigger_conn_request(struct rvt_qp *qp, u32 bth1) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci struct hfi1_qp_priv *priv = qp->priv; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci if (!priv->opfn.extended && hfi1_opfn_extended(bth1) && 29862306a36Sopenharmony_ci HFI1_CAP_IS_KSET(OPFN)) { 29962306a36Sopenharmony_ci priv->opfn.extended = 1; 30062306a36Sopenharmony_ci if (qp->state == IB_QPS_RTS) 30162306a36Sopenharmony_ci opfn_conn_request(qp); 30262306a36Sopenharmony_ci } 30362306a36Sopenharmony_ci} 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ciint opfn_init(void) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci opfn_wq = alloc_workqueue("hfi_opfn", 30862306a36Sopenharmony_ci WQ_SYSFS | WQ_HIGHPRI | WQ_CPU_INTENSIVE | 30962306a36Sopenharmony_ci WQ_MEM_RECLAIM, 31062306a36Sopenharmony_ci HFI1_MAX_ACTIVE_WORKQUEUE_ENTRIES); 31162306a36Sopenharmony_ci if (!opfn_wq) 31262306a36Sopenharmony_ci return -ENOMEM; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci return 0; 31562306a36Sopenharmony_ci} 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_civoid opfn_exit(void) 31862306a36Sopenharmony_ci{ 31962306a36Sopenharmony_ci if (opfn_wq) { 32062306a36Sopenharmony_ci destroy_workqueue(opfn_wq); 32162306a36Sopenharmony_ci opfn_wq = NULL; 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci} 324