18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * RDMA Transport Layer 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2014 - 2018 ProfitBricks GmbH. All rights reserved. 68c2ecf20Sopenharmony_ci * Copyright (c) 2018 - 2019 1&1 IONOS Cloud GmbH. All rights reserved. 78c2ecf20Sopenharmony_ci * Copyright (c) 2019 - 2020 1&1 IONOS SE. All rights reserved. 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci#undef pr_fmt 108c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME " L" __stringify(__LINE__) ": " fmt 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/inet.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include "rtrs-pri.h" 168c2ecf20Sopenharmony_ci#include "rtrs-log.h" 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("RDMA Transport Core"); 198c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistruct rtrs_iu *rtrs_iu_alloc(u32 queue_size, size_t size, gfp_t gfp_mask, 228c2ecf20Sopenharmony_ci struct ib_device *dma_dev, 238c2ecf20Sopenharmony_ci enum dma_data_direction dir, 248c2ecf20Sopenharmony_ci void (*done)(struct ib_cq *cq, struct ib_wc *wc)) 258c2ecf20Sopenharmony_ci{ 268c2ecf20Sopenharmony_ci struct rtrs_iu *ius, *iu; 278c2ecf20Sopenharmony_ci int i; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci ius = kcalloc(queue_size, sizeof(*ius), gfp_mask); 308c2ecf20Sopenharmony_ci if (!ius) 318c2ecf20Sopenharmony_ci return NULL; 328c2ecf20Sopenharmony_ci for (i = 0; i < queue_size; i++) { 338c2ecf20Sopenharmony_ci iu = &ius[i]; 348c2ecf20Sopenharmony_ci iu->direction = dir; 358c2ecf20Sopenharmony_ci iu->buf = kzalloc(size, gfp_mask); 368c2ecf20Sopenharmony_ci if (!iu->buf) 378c2ecf20Sopenharmony_ci goto err; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci iu->dma_addr = ib_dma_map_single(dma_dev, iu->buf, size, dir); 408c2ecf20Sopenharmony_ci if (ib_dma_mapping_error(dma_dev, iu->dma_addr)) { 418c2ecf20Sopenharmony_ci kfree(iu->buf); 428c2ecf20Sopenharmony_ci goto err; 438c2ecf20Sopenharmony_ci } 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci iu->cqe.done = done; 468c2ecf20Sopenharmony_ci iu->size = size; 478c2ecf20Sopenharmony_ci } 488c2ecf20Sopenharmony_ci return ius; 498c2ecf20Sopenharmony_cierr: 508c2ecf20Sopenharmony_ci rtrs_iu_free(ius, dma_dev, i); 518c2ecf20Sopenharmony_ci return NULL; 528c2ecf20Sopenharmony_ci} 538c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rtrs_iu_alloc); 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_civoid rtrs_iu_free(struct rtrs_iu *ius, struct ib_device *ibdev, u32 queue_size) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci struct rtrs_iu *iu; 588c2ecf20Sopenharmony_ci int i; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci if (!ius) 618c2ecf20Sopenharmony_ci return; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci for (i = 0; i < queue_size; i++) { 648c2ecf20Sopenharmony_ci iu = &ius[i]; 658c2ecf20Sopenharmony_ci ib_dma_unmap_single(ibdev, iu->dma_addr, iu->size, iu->direction); 668c2ecf20Sopenharmony_ci kfree(iu->buf); 678c2ecf20Sopenharmony_ci } 688c2ecf20Sopenharmony_ci kfree(ius); 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rtrs_iu_free); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ciint rtrs_iu_post_recv(struct rtrs_con *con, struct rtrs_iu *iu) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci struct rtrs_sess *sess = con->sess; 758c2ecf20Sopenharmony_ci struct ib_recv_wr wr; 768c2ecf20Sopenharmony_ci struct ib_sge list; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci list.addr = iu->dma_addr; 798c2ecf20Sopenharmony_ci list.length = iu->size; 808c2ecf20Sopenharmony_ci list.lkey = sess->dev->ib_pd->local_dma_lkey; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci if (list.length == 0) { 838c2ecf20Sopenharmony_ci rtrs_wrn(con->sess, 848c2ecf20Sopenharmony_ci "Posting receive work request failed, sg list is empty\n"); 858c2ecf20Sopenharmony_ci return -EINVAL; 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci wr = (struct ib_recv_wr) { 888c2ecf20Sopenharmony_ci .wr_cqe = &iu->cqe, 898c2ecf20Sopenharmony_ci .sg_list = &list, 908c2ecf20Sopenharmony_ci .num_sge = 1, 918c2ecf20Sopenharmony_ci }; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci return ib_post_recv(con->qp, &wr, NULL); 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rtrs_iu_post_recv); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ciint rtrs_post_recv_empty(struct rtrs_con *con, struct ib_cqe *cqe) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci struct ib_recv_wr wr; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci wr = (struct ib_recv_wr) { 1028c2ecf20Sopenharmony_ci .wr_cqe = cqe, 1038c2ecf20Sopenharmony_ci }; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci return ib_post_recv(con->qp, &wr, NULL); 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rtrs_post_recv_empty); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic int rtrs_post_send(struct ib_qp *qp, struct ib_send_wr *head, 1108c2ecf20Sopenharmony_ci struct ib_send_wr *wr) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci if (head) { 1138c2ecf20Sopenharmony_ci struct ib_send_wr *tail = head; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci while (tail->next) 1168c2ecf20Sopenharmony_ci tail = tail->next; 1178c2ecf20Sopenharmony_ci tail->next = wr; 1188c2ecf20Sopenharmony_ci } else { 1198c2ecf20Sopenharmony_ci head = wr; 1208c2ecf20Sopenharmony_ci } 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci return ib_post_send(qp, head, NULL); 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ciint rtrs_iu_post_send(struct rtrs_con *con, struct rtrs_iu *iu, size_t size, 1268c2ecf20Sopenharmony_ci struct ib_send_wr *head) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci struct rtrs_sess *sess = con->sess; 1298c2ecf20Sopenharmony_ci struct ib_send_wr wr; 1308c2ecf20Sopenharmony_ci struct ib_sge list; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci if (WARN_ON(size == 0)) 1338c2ecf20Sopenharmony_ci return -EINVAL; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci list.addr = iu->dma_addr; 1368c2ecf20Sopenharmony_ci list.length = size; 1378c2ecf20Sopenharmony_ci list.lkey = sess->dev->ib_pd->local_dma_lkey; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci wr = (struct ib_send_wr) { 1408c2ecf20Sopenharmony_ci .wr_cqe = &iu->cqe, 1418c2ecf20Sopenharmony_ci .sg_list = &list, 1428c2ecf20Sopenharmony_ci .num_sge = 1, 1438c2ecf20Sopenharmony_ci .opcode = IB_WR_SEND, 1448c2ecf20Sopenharmony_ci .send_flags = IB_SEND_SIGNALED, 1458c2ecf20Sopenharmony_ci }; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci return rtrs_post_send(con->qp, head, &wr); 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rtrs_iu_post_send); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ciint rtrs_iu_post_rdma_write_imm(struct rtrs_con *con, struct rtrs_iu *iu, 1528c2ecf20Sopenharmony_ci struct ib_sge *sge, unsigned int num_sge, 1538c2ecf20Sopenharmony_ci u32 rkey, u64 rdma_addr, u32 imm_data, 1548c2ecf20Sopenharmony_ci enum ib_send_flags flags, 1558c2ecf20Sopenharmony_ci struct ib_send_wr *head) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci struct ib_rdma_wr wr; 1588c2ecf20Sopenharmony_ci int i; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci wr = (struct ib_rdma_wr) { 1618c2ecf20Sopenharmony_ci .wr.wr_cqe = &iu->cqe, 1628c2ecf20Sopenharmony_ci .wr.sg_list = sge, 1638c2ecf20Sopenharmony_ci .wr.num_sge = num_sge, 1648c2ecf20Sopenharmony_ci .rkey = rkey, 1658c2ecf20Sopenharmony_ci .remote_addr = rdma_addr, 1668c2ecf20Sopenharmony_ci .wr.opcode = IB_WR_RDMA_WRITE_WITH_IMM, 1678c2ecf20Sopenharmony_ci .wr.ex.imm_data = cpu_to_be32(imm_data), 1688c2ecf20Sopenharmony_ci .wr.send_flags = flags, 1698c2ecf20Sopenharmony_ci }; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci /* 1728c2ecf20Sopenharmony_ci * If one of the sges has 0 size, the operation will fail with a 1738c2ecf20Sopenharmony_ci * length error 1748c2ecf20Sopenharmony_ci */ 1758c2ecf20Sopenharmony_ci for (i = 0; i < num_sge; i++) 1768c2ecf20Sopenharmony_ci if (WARN_ON(sge[i].length == 0)) 1778c2ecf20Sopenharmony_ci return -EINVAL; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci return rtrs_post_send(con->qp, head, &wr.wr); 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rtrs_iu_post_rdma_write_imm); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ciint rtrs_post_rdma_write_imm_empty(struct rtrs_con *con, struct ib_cqe *cqe, 1848c2ecf20Sopenharmony_ci u32 imm_data, enum ib_send_flags flags, 1858c2ecf20Sopenharmony_ci struct ib_send_wr *head) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci struct ib_rdma_wr wr; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci wr = (struct ib_rdma_wr) { 1908c2ecf20Sopenharmony_ci .wr.wr_cqe = cqe, 1918c2ecf20Sopenharmony_ci .wr.send_flags = flags, 1928c2ecf20Sopenharmony_ci .wr.opcode = IB_WR_RDMA_WRITE_WITH_IMM, 1938c2ecf20Sopenharmony_ci .wr.ex.imm_data = cpu_to_be32(imm_data), 1948c2ecf20Sopenharmony_ci }; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci return rtrs_post_send(con->qp, head, &wr.wr); 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rtrs_post_rdma_write_imm_empty); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cistatic void qp_event_handler(struct ib_event *ev, void *ctx) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci struct rtrs_con *con = ctx; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci switch (ev->event) { 2058c2ecf20Sopenharmony_ci case IB_EVENT_COMM_EST: 2068c2ecf20Sopenharmony_ci rtrs_info(con->sess, "QP event %s (%d) received\n", 2078c2ecf20Sopenharmony_ci ib_event_msg(ev->event), ev->event); 2088c2ecf20Sopenharmony_ci rdma_notify(con->cm_id, IB_EVENT_COMM_EST); 2098c2ecf20Sopenharmony_ci break; 2108c2ecf20Sopenharmony_ci default: 2118c2ecf20Sopenharmony_ci rtrs_info(con->sess, "Unhandled QP event %s (%d) received\n", 2128c2ecf20Sopenharmony_ci ib_event_msg(ev->event), ev->event); 2138c2ecf20Sopenharmony_ci break; 2148c2ecf20Sopenharmony_ci } 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cistatic int create_cq(struct rtrs_con *con, int cq_vector, u16 cq_size, 2188c2ecf20Sopenharmony_ci enum ib_poll_context poll_ctx) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci struct rdma_cm_id *cm_id = con->cm_id; 2218c2ecf20Sopenharmony_ci struct ib_cq *cq; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci cq = ib_alloc_cq(cm_id->device, con, cq_size, 2248c2ecf20Sopenharmony_ci cq_vector, poll_ctx); 2258c2ecf20Sopenharmony_ci if (IS_ERR(cq)) { 2268c2ecf20Sopenharmony_ci rtrs_err(con->sess, "Creating completion queue failed, errno: %ld\n", 2278c2ecf20Sopenharmony_ci PTR_ERR(cq)); 2288c2ecf20Sopenharmony_ci return PTR_ERR(cq); 2298c2ecf20Sopenharmony_ci } 2308c2ecf20Sopenharmony_ci con->cq = cq; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci return 0; 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cistatic int create_qp(struct rtrs_con *con, struct ib_pd *pd, 2368c2ecf20Sopenharmony_ci u32 max_send_wr, u32 max_recv_wr, u32 max_sge) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci struct ib_qp_init_attr init_attr = {NULL}; 2398c2ecf20Sopenharmony_ci struct rdma_cm_id *cm_id = con->cm_id; 2408c2ecf20Sopenharmony_ci int ret; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci init_attr.cap.max_send_wr = max_send_wr; 2438c2ecf20Sopenharmony_ci init_attr.cap.max_recv_wr = max_recv_wr; 2448c2ecf20Sopenharmony_ci init_attr.cap.max_recv_sge = 1; 2458c2ecf20Sopenharmony_ci init_attr.event_handler = qp_event_handler; 2468c2ecf20Sopenharmony_ci init_attr.qp_context = con; 2478c2ecf20Sopenharmony_ci init_attr.cap.max_send_sge = max_sge; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci init_attr.qp_type = IB_QPT_RC; 2508c2ecf20Sopenharmony_ci init_attr.send_cq = con->cq; 2518c2ecf20Sopenharmony_ci init_attr.recv_cq = con->cq; 2528c2ecf20Sopenharmony_ci init_attr.sq_sig_type = IB_SIGNAL_REQ_WR; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci ret = rdma_create_qp(cm_id, pd, &init_attr); 2558c2ecf20Sopenharmony_ci if (ret) { 2568c2ecf20Sopenharmony_ci rtrs_err(con->sess, "Creating QP failed, err: %d\n", ret); 2578c2ecf20Sopenharmony_ci return ret; 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci con->qp = cm_id->qp; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci return ret; 2628c2ecf20Sopenharmony_ci} 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ciint rtrs_cq_qp_create(struct rtrs_sess *sess, struct rtrs_con *con, 2658c2ecf20Sopenharmony_ci u32 max_send_sge, int cq_vector, int cq_size, 2668c2ecf20Sopenharmony_ci u32 max_send_wr, u32 max_recv_wr, 2678c2ecf20Sopenharmony_ci enum ib_poll_context poll_ctx) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci int err; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci err = create_cq(con, cq_vector, cq_size, poll_ctx); 2728c2ecf20Sopenharmony_ci if (err) 2738c2ecf20Sopenharmony_ci return err; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci err = create_qp(con, sess->dev->ib_pd, max_send_wr, max_recv_wr, 2768c2ecf20Sopenharmony_ci max_send_sge); 2778c2ecf20Sopenharmony_ci if (err) { 2788c2ecf20Sopenharmony_ci ib_free_cq(con->cq); 2798c2ecf20Sopenharmony_ci con->cq = NULL; 2808c2ecf20Sopenharmony_ci return err; 2818c2ecf20Sopenharmony_ci } 2828c2ecf20Sopenharmony_ci con->sess = sess; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci return 0; 2858c2ecf20Sopenharmony_ci} 2868c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rtrs_cq_qp_create); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_civoid rtrs_cq_qp_destroy(struct rtrs_con *con) 2898c2ecf20Sopenharmony_ci{ 2908c2ecf20Sopenharmony_ci if (con->qp) { 2918c2ecf20Sopenharmony_ci rdma_destroy_qp(con->cm_id); 2928c2ecf20Sopenharmony_ci con->qp = NULL; 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci if (con->cq) { 2958c2ecf20Sopenharmony_ci ib_free_cq(con->cq); 2968c2ecf20Sopenharmony_ci con->cq = NULL; 2978c2ecf20Sopenharmony_ci } 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rtrs_cq_qp_destroy); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_cistatic void schedule_hb(struct rtrs_sess *sess) 3028c2ecf20Sopenharmony_ci{ 3038c2ecf20Sopenharmony_ci queue_delayed_work(sess->hb_wq, &sess->hb_dwork, 3048c2ecf20Sopenharmony_ci msecs_to_jiffies(sess->hb_interval_ms)); 3058c2ecf20Sopenharmony_ci} 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_civoid rtrs_send_hb_ack(struct rtrs_sess *sess) 3088c2ecf20Sopenharmony_ci{ 3098c2ecf20Sopenharmony_ci struct rtrs_con *usr_con = sess->con[0]; 3108c2ecf20Sopenharmony_ci u32 imm; 3118c2ecf20Sopenharmony_ci int err; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci imm = rtrs_to_imm(RTRS_HB_ACK_IMM, 0); 3148c2ecf20Sopenharmony_ci err = rtrs_post_rdma_write_imm_empty(usr_con, sess->hb_cqe, imm, 3158c2ecf20Sopenharmony_ci 0, NULL); 3168c2ecf20Sopenharmony_ci if (err) { 3178c2ecf20Sopenharmony_ci sess->hb_err_handler(usr_con); 3188c2ecf20Sopenharmony_ci return; 3198c2ecf20Sopenharmony_ci } 3208c2ecf20Sopenharmony_ci} 3218c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rtrs_send_hb_ack); 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_cistatic void hb_work(struct work_struct *work) 3248c2ecf20Sopenharmony_ci{ 3258c2ecf20Sopenharmony_ci struct rtrs_con *usr_con; 3268c2ecf20Sopenharmony_ci struct rtrs_sess *sess; 3278c2ecf20Sopenharmony_ci u32 imm; 3288c2ecf20Sopenharmony_ci int err; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci sess = container_of(to_delayed_work(work), typeof(*sess), hb_dwork); 3318c2ecf20Sopenharmony_ci usr_con = sess->con[0]; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci if (sess->hb_missed_cnt > sess->hb_missed_max) { 3348c2ecf20Sopenharmony_ci sess->hb_err_handler(usr_con); 3358c2ecf20Sopenharmony_ci return; 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci if (sess->hb_missed_cnt++) { 3388c2ecf20Sopenharmony_ci /* Reschedule work without sending hb */ 3398c2ecf20Sopenharmony_ci schedule_hb(sess); 3408c2ecf20Sopenharmony_ci return; 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci imm = rtrs_to_imm(RTRS_HB_MSG_IMM, 0); 3438c2ecf20Sopenharmony_ci err = rtrs_post_rdma_write_imm_empty(usr_con, sess->hb_cqe, imm, 3448c2ecf20Sopenharmony_ci 0, NULL); 3458c2ecf20Sopenharmony_ci if (err) { 3468c2ecf20Sopenharmony_ci sess->hb_err_handler(usr_con); 3478c2ecf20Sopenharmony_ci return; 3488c2ecf20Sopenharmony_ci } 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci schedule_hb(sess); 3518c2ecf20Sopenharmony_ci} 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_civoid rtrs_init_hb(struct rtrs_sess *sess, struct ib_cqe *cqe, 3548c2ecf20Sopenharmony_ci unsigned int interval_ms, unsigned int missed_max, 3558c2ecf20Sopenharmony_ci void (*err_handler)(struct rtrs_con *con), 3568c2ecf20Sopenharmony_ci struct workqueue_struct *wq) 3578c2ecf20Sopenharmony_ci{ 3588c2ecf20Sopenharmony_ci sess->hb_cqe = cqe; 3598c2ecf20Sopenharmony_ci sess->hb_interval_ms = interval_ms; 3608c2ecf20Sopenharmony_ci sess->hb_err_handler = err_handler; 3618c2ecf20Sopenharmony_ci sess->hb_wq = wq; 3628c2ecf20Sopenharmony_ci sess->hb_missed_max = missed_max; 3638c2ecf20Sopenharmony_ci sess->hb_missed_cnt = 0; 3648c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&sess->hb_dwork, hb_work); 3658c2ecf20Sopenharmony_ci} 3668c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rtrs_init_hb); 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_civoid rtrs_start_hb(struct rtrs_sess *sess) 3698c2ecf20Sopenharmony_ci{ 3708c2ecf20Sopenharmony_ci schedule_hb(sess); 3718c2ecf20Sopenharmony_ci} 3728c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rtrs_start_hb); 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_civoid rtrs_stop_hb(struct rtrs_sess *sess) 3758c2ecf20Sopenharmony_ci{ 3768c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&sess->hb_dwork); 3778c2ecf20Sopenharmony_ci sess->hb_missed_cnt = 0; 3788c2ecf20Sopenharmony_ci} 3798c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rtrs_stop_hb); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_cistatic int rtrs_str_gid_to_sockaddr(const char *addr, size_t len, 3828c2ecf20Sopenharmony_ci short port, struct sockaddr_storage *dst) 3838c2ecf20Sopenharmony_ci{ 3848c2ecf20Sopenharmony_ci struct sockaddr_ib *dst_ib = (struct sockaddr_ib *)dst; 3858c2ecf20Sopenharmony_ci int ret; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci /* 3888c2ecf20Sopenharmony_ci * We can use some of the IPv6 functions since GID is a valid 3898c2ecf20Sopenharmony_ci * IPv6 address format 3908c2ecf20Sopenharmony_ci */ 3918c2ecf20Sopenharmony_ci ret = in6_pton(addr, len, dst_ib->sib_addr.sib_raw, '\0', NULL); 3928c2ecf20Sopenharmony_ci if (ret == 0) 3938c2ecf20Sopenharmony_ci return -EINVAL; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci dst_ib->sib_family = AF_IB; 3968c2ecf20Sopenharmony_ci /* 3978c2ecf20Sopenharmony_ci * Use the same TCP server port number as the IB service ID 3988c2ecf20Sopenharmony_ci * on the IB port space range 3998c2ecf20Sopenharmony_ci */ 4008c2ecf20Sopenharmony_ci dst_ib->sib_sid = cpu_to_be64(RDMA_IB_IP_PS_IB | port); 4018c2ecf20Sopenharmony_ci dst_ib->sib_sid_mask = cpu_to_be64(0xffffffffffffffffULL); 4028c2ecf20Sopenharmony_ci dst_ib->sib_pkey = cpu_to_be16(0xffff); 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci return 0; 4058c2ecf20Sopenharmony_ci} 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci/** 4088c2ecf20Sopenharmony_ci * rtrs_str_to_sockaddr() - Convert rtrs address string to sockaddr 4098c2ecf20Sopenharmony_ci * @addr: String representation of an addr (IPv4, IPv6 or IB GID): 4108c2ecf20Sopenharmony_ci * - "ip:192.168.1.1" 4118c2ecf20Sopenharmony_ci * - "ip:fe80::200:5aee:feaa:20a2" 4128c2ecf20Sopenharmony_ci * - "gid:fe80::200:5aee:feaa:20a2" 4138c2ecf20Sopenharmony_ci * @len: String address length 4148c2ecf20Sopenharmony_ci * @port: Destination port 4158c2ecf20Sopenharmony_ci * @dst: Destination sockaddr structure 4168c2ecf20Sopenharmony_ci * 4178c2ecf20Sopenharmony_ci * Returns 0 if conversion successful. Non-zero on error. 4188c2ecf20Sopenharmony_ci */ 4198c2ecf20Sopenharmony_cistatic int rtrs_str_to_sockaddr(const char *addr, size_t len, 4208c2ecf20Sopenharmony_ci u16 port, struct sockaddr_storage *dst) 4218c2ecf20Sopenharmony_ci{ 4228c2ecf20Sopenharmony_ci if (strncmp(addr, "gid:", 4) == 0) { 4238c2ecf20Sopenharmony_ci return rtrs_str_gid_to_sockaddr(addr + 4, len - 4, port, dst); 4248c2ecf20Sopenharmony_ci } else if (strncmp(addr, "ip:", 3) == 0) { 4258c2ecf20Sopenharmony_ci char port_str[8]; 4268c2ecf20Sopenharmony_ci char *cpy; 4278c2ecf20Sopenharmony_ci int err; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci snprintf(port_str, sizeof(port_str), "%u", port); 4308c2ecf20Sopenharmony_ci cpy = kstrndup(addr + 3, len - 3, GFP_KERNEL); 4318c2ecf20Sopenharmony_ci err = cpy ? inet_pton_with_scope(&init_net, AF_UNSPEC, 4328c2ecf20Sopenharmony_ci cpy, port_str, dst) : -ENOMEM; 4338c2ecf20Sopenharmony_ci kfree(cpy); 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci return err; 4368c2ecf20Sopenharmony_ci } 4378c2ecf20Sopenharmony_ci return -EPROTONOSUPPORT; 4388c2ecf20Sopenharmony_ci} 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci/** 4418c2ecf20Sopenharmony_ci * sockaddr_to_str() - convert sockaddr to a string. 4428c2ecf20Sopenharmony_ci * @addr: the sockadddr structure to be converted. 4438c2ecf20Sopenharmony_ci * @buf: string containing socket addr. 4448c2ecf20Sopenharmony_ci * @len: string length. 4458c2ecf20Sopenharmony_ci * 4468c2ecf20Sopenharmony_ci * The return value is the number of characters written into buf not 4478c2ecf20Sopenharmony_ci * including the trailing '\0'. If len is == 0 the function returns 0.. 4488c2ecf20Sopenharmony_ci */ 4498c2ecf20Sopenharmony_ciint sockaddr_to_str(const struct sockaddr *addr, char *buf, size_t len) 4508c2ecf20Sopenharmony_ci{ 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci switch (addr->sa_family) { 4538c2ecf20Sopenharmony_ci case AF_IB: 4548c2ecf20Sopenharmony_ci return scnprintf(buf, len, "gid:%pI6", 4558c2ecf20Sopenharmony_ci &((struct sockaddr_ib *)addr)->sib_addr.sib_raw); 4568c2ecf20Sopenharmony_ci case AF_INET: 4578c2ecf20Sopenharmony_ci return scnprintf(buf, len, "ip:%pI4", 4588c2ecf20Sopenharmony_ci &((struct sockaddr_in *)addr)->sin_addr); 4598c2ecf20Sopenharmony_ci case AF_INET6: 4608c2ecf20Sopenharmony_ci return scnprintf(buf, len, "ip:%pI6c", 4618c2ecf20Sopenharmony_ci &((struct sockaddr_in6 *)addr)->sin6_addr); 4628c2ecf20Sopenharmony_ci } 4638c2ecf20Sopenharmony_ci return scnprintf(buf, len, "<invalid address family>"); 4648c2ecf20Sopenharmony_ci} 4658c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sockaddr_to_str); 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci/** 4688c2ecf20Sopenharmony_ci * rtrs_addr_to_sockaddr() - convert path string "src,dst" or "src@dst" 4698c2ecf20Sopenharmony_ci * to sockaddreses 4708c2ecf20Sopenharmony_ci * @str: string containing source and destination addr of a path 4718c2ecf20Sopenharmony_ci * separated by ',' or '@' I.e. "ip:1.1.1.1,ip:1.1.1.2" or 4728c2ecf20Sopenharmony_ci * "ip:1.1.1.1@ip:1.1.1.2". If str contains only one address it's 4738c2ecf20Sopenharmony_ci * considered to be destination. 4748c2ecf20Sopenharmony_ci * @len: string length 4758c2ecf20Sopenharmony_ci * @port: Destination port number. 4768c2ecf20Sopenharmony_ci * @addr: will be set to the source/destination address or to NULL 4778c2ecf20Sopenharmony_ci * if str doesn't contain any source address. 4788c2ecf20Sopenharmony_ci * 4798c2ecf20Sopenharmony_ci * Returns zero if conversion successful. Non-zero otherwise. 4808c2ecf20Sopenharmony_ci */ 4818c2ecf20Sopenharmony_ciint rtrs_addr_to_sockaddr(const char *str, size_t len, u16 port, 4828c2ecf20Sopenharmony_ci struct rtrs_addr *addr) 4838c2ecf20Sopenharmony_ci{ 4848c2ecf20Sopenharmony_ci const char *d; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci d = strchr(str, ','); 4878c2ecf20Sopenharmony_ci if (!d) 4888c2ecf20Sopenharmony_ci d = strchr(str, '@'); 4898c2ecf20Sopenharmony_ci if (d) { 4908c2ecf20Sopenharmony_ci if (rtrs_str_to_sockaddr(str, d - str, 0, addr->src)) 4918c2ecf20Sopenharmony_ci return -EINVAL; 4928c2ecf20Sopenharmony_ci d += 1; 4938c2ecf20Sopenharmony_ci len -= d - str; 4948c2ecf20Sopenharmony_ci str = d; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci } else { 4978c2ecf20Sopenharmony_ci addr->src = NULL; 4988c2ecf20Sopenharmony_ci } 4998c2ecf20Sopenharmony_ci return rtrs_str_to_sockaddr(str, len, port, addr->dst); 5008c2ecf20Sopenharmony_ci} 5018c2ecf20Sopenharmony_ciEXPORT_SYMBOL(rtrs_addr_to_sockaddr); 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_civoid rtrs_rdma_dev_pd_init(enum ib_pd_flags pd_flags, 5048c2ecf20Sopenharmony_ci struct rtrs_rdma_dev_pd *pool) 5058c2ecf20Sopenharmony_ci{ 5068c2ecf20Sopenharmony_ci WARN_ON(pool->ops && (!pool->ops->alloc ^ !pool->ops->free)); 5078c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&pool->list); 5088c2ecf20Sopenharmony_ci mutex_init(&pool->mutex); 5098c2ecf20Sopenharmony_ci pool->pd_flags = pd_flags; 5108c2ecf20Sopenharmony_ci} 5118c2ecf20Sopenharmony_ciEXPORT_SYMBOL(rtrs_rdma_dev_pd_init); 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_civoid rtrs_rdma_dev_pd_deinit(struct rtrs_rdma_dev_pd *pool) 5148c2ecf20Sopenharmony_ci{ 5158c2ecf20Sopenharmony_ci mutex_destroy(&pool->mutex); 5168c2ecf20Sopenharmony_ci WARN_ON(!list_empty(&pool->list)); 5178c2ecf20Sopenharmony_ci} 5188c2ecf20Sopenharmony_ciEXPORT_SYMBOL(rtrs_rdma_dev_pd_deinit); 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_cistatic void dev_free(struct kref *ref) 5218c2ecf20Sopenharmony_ci{ 5228c2ecf20Sopenharmony_ci struct rtrs_rdma_dev_pd *pool; 5238c2ecf20Sopenharmony_ci struct rtrs_ib_dev *dev; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci dev = container_of(ref, typeof(*dev), ref); 5268c2ecf20Sopenharmony_ci pool = dev->pool; 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci mutex_lock(&pool->mutex); 5298c2ecf20Sopenharmony_ci list_del(&dev->entry); 5308c2ecf20Sopenharmony_ci mutex_unlock(&pool->mutex); 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci if (pool->ops && pool->ops->deinit) 5338c2ecf20Sopenharmony_ci pool->ops->deinit(dev); 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci ib_dealloc_pd(dev->ib_pd); 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci if (pool->ops && pool->ops->free) 5388c2ecf20Sopenharmony_ci pool->ops->free(dev); 5398c2ecf20Sopenharmony_ci else 5408c2ecf20Sopenharmony_ci kfree(dev); 5418c2ecf20Sopenharmony_ci} 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ciint rtrs_ib_dev_put(struct rtrs_ib_dev *dev) 5448c2ecf20Sopenharmony_ci{ 5458c2ecf20Sopenharmony_ci return kref_put(&dev->ref, dev_free); 5468c2ecf20Sopenharmony_ci} 5478c2ecf20Sopenharmony_ciEXPORT_SYMBOL(rtrs_ib_dev_put); 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_cistatic int rtrs_ib_dev_get(struct rtrs_ib_dev *dev) 5508c2ecf20Sopenharmony_ci{ 5518c2ecf20Sopenharmony_ci return kref_get_unless_zero(&dev->ref); 5528c2ecf20Sopenharmony_ci} 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_cistruct rtrs_ib_dev * 5558c2ecf20Sopenharmony_cirtrs_ib_dev_find_or_add(struct ib_device *ib_dev, 5568c2ecf20Sopenharmony_ci struct rtrs_rdma_dev_pd *pool) 5578c2ecf20Sopenharmony_ci{ 5588c2ecf20Sopenharmony_ci struct rtrs_ib_dev *dev; 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci mutex_lock(&pool->mutex); 5618c2ecf20Sopenharmony_ci list_for_each_entry(dev, &pool->list, entry) { 5628c2ecf20Sopenharmony_ci if (dev->ib_dev->node_guid == ib_dev->node_guid && 5638c2ecf20Sopenharmony_ci rtrs_ib_dev_get(dev)) 5648c2ecf20Sopenharmony_ci goto out_unlock; 5658c2ecf20Sopenharmony_ci } 5668c2ecf20Sopenharmony_ci mutex_unlock(&pool->mutex); 5678c2ecf20Sopenharmony_ci if (pool->ops && pool->ops->alloc) 5688c2ecf20Sopenharmony_ci dev = pool->ops->alloc(); 5698c2ecf20Sopenharmony_ci else 5708c2ecf20Sopenharmony_ci dev = kzalloc(sizeof(*dev), GFP_KERNEL); 5718c2ecf20Sopenharmony_ci if (IS_ERR_OR_NULL(dev)) 5728c2ecf20Sopenharmony_ci goto out_err; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci kref_init(&dev->ref); 5758c2ecf20Sopenharmony_ci dev->pool = pool; 5768c2ecf20Sopenharmony_ci dev->ib_dev = ib_dev; 5778c2ecf20Sopenharmony_ci dev->ib_pd = ib_alloc_pd(ib_dev, pool->pd_flags); 5788c2ecf20Sopenharmony_ci if (IS_ERR(dev->ib_pd)) 5798c2ecf20Sopenharmony_ci goto out_free_dev; 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci if (pool->ops && pool->ops->init && pool->ops->init(dev)) 5828c2ecf20Sopenharmony_ci goto out_free_pd; 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci mutex_lock(&pool->mutex); 5858c2ecf20Sopenharmony_ci list_add(&dev->entry, &pool->list); 5868c2ecf20Sopenharmony_ciout_unlock: 5878c2ecf20Sopenharmony_ci mutex_unlock(&pool->mutex); 5888c2ecf20Sopenharmony_ci return dev; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ciout_free_pd: 5918c2ecf20Sopenharmony_ci ib_dealloc_pd(dev->ib_pd); 5928c2ecf20Sopenharmony_ciout_free_dev: 5938c2ecf20Sopenharmony_ci if (pool->ops && pool->ops->free) 5948c2ecf20Sopenharmony_ci pool->ops->free(dev); 5958c2ecf20Sopenharmony_ci else 5968c2ecf20Sopenharmony_ci kfree(dev); 5978c2ecf20Sopenharmony_ciout_err: 5988c2ecf20Sopenharmony_ci return NULL; 5998c2ecf20Sopenharmony_ci} 6008c2ecf20Sopenharmony_ciEXPORT_SYMBOL(rtrs_ib_dev_find_or_add); 601